diff --git a/packages/site_shared/lib/_sass/base/_reset.scss b/packages/site_shared/lib/_sass/base/_reset.scss index 61d4b5cdd8..a4e30a08c6 100644 --- a/packages/site_shared/lib/_sass/base/_reset.scss +++ b/packages/site_shared/lib/_sass/base/_reset.scss @@ -15,6 +15,7 @@ body { button, input, +optgroup, select, textarea { font-family: inherit; @@ -27,4 +28,4 @@ textarea { color: inherit; margin: 0; padding: 0; -} +} \ No newline at end of file diff --git a/packages/site_shared/lib/_sass/components/_blog.scss b/packages/site_shared/lib/_sass/components/_blog.scss new file mode 100644 index 0000000000..33ffff8982 --- /dev/null +++ b/packages/site_shared/lib/_sass/components/_blog.scss @@ -0,0 +1,501 @@ +.blog-index { + display: flex; + flex-direction: column; + gap: 2rem; + width: 100%; + max-width: 1400px; + margin: 0 auto; + box-sizing: border-box; +} + +.blog-index-toolbar { + display: flex; + align-items: stretch; + gap: 1rem; + border-bottom: 1px solid var(--site-outline); + margin-bottom: 2rem; + font-family: var(--site-ui-fontFamily); + + .blog-feed-link { + display: inline-flex; + align-items: center; + gap: 0.4rem; + align-self: center; + padding: 0.25rem; + white-space: nowrap; + color: var(--site-base-fgColor-alt); + text-decoration: none; + font-weight: 500; + + &:hover { + color: var(--site-base-fgColor); + text-decoration: none; + } + + .material-symbols { + font-size: 1.25rem; + } + + .blog-feed-link-label { + display: none; + + @media (min-width: 576px) { + display: block; + } + } + } +} + +.blog-categories { + display: flex; + justify-content: flex-start; + flex: 1; + min-width: 0; + gap: 1rem; + overflow-x: auto; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; + + &::-webkit-scrollbar { + display: none; + } + + .category-separator { + align-self: center; + min-width: 1px; + height: 1.25rem; + background-color: var(--site-outline); + margin: 0 -0.5rem; + pointer-events: none; + user-select: none; + } +} + +.blog-category-chip { + border: none; + border-bottom: 2px solid transparent; + cursor: pointer; + transition: color 0.2s ease, border-color 0.2s ease; + white-space: nowrap; + margin-bottom: -1px; + padding: 2px; + + &:hover button { + color: var(--site-base-fgColor); + background-color: transparent; + } + + &.active { + border-bottom-color: var(--site-base-fgColor); + + button { + background-color: transparent; + color: var(--site-base-fgColor); + } + } + + button { + padding: 0.75rem 0.5rem; + + font-size: 1rem; + font-weight: 500; + background-color: transparent; + color: var(--site-base-fgColor-alt); + } +} + +.blog-posts-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 2rem; + + @media (max-width: 768px) { + grid-template-columns: 1fr; + } + + .blog-card:not(.layout-featured):not(.layout-grid):not(.layout-list) { + display: none; + } +} + +.blog-card { + display: flex; + flex-direction: column; + gap: 0.5rem; + background-color: var(--site-bg-color, transparent); + padding: 1.5rem 0; + border-bottom: 1px solid var(--site-outline); + overflow: hidden; + + &, + &:hover, + &:active, + &:focus { + text-decoration: none; + color: inherit; + } + + .blog-card-image { + width: 100%; + aspect-ratio: 16/9; + overflow: hidden; + border-radius: 8px; + + img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; + } + } + + .blog-card-title { + font-weight: 800; + font-size: 1.5rem; + line-height: 1.2; + margin-top: 0.25rem; + margin-bottom: 0.5rem; + color: var(--site-base-fgColor); + + a { + color: inherit; + text-decoration: none; + + &:hover { + color: var(--site-primary-color); + } + } + } + + .blog-card-content { + flex: 1; + display: flex; + flex-direction: column; + } + + .blog-card-spacer { + flex: 1; + } + + .blog-card-description { + font-size: 1rem; + color: var(--site-base-fgColor-alt); + margin-bottom: 1rem; + line-height: 1.5; + display: -webkit-box; + -webkit-line-clamp: 3; + line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + } + + &.layout-featured { + grid-column: 1 / -1; + + @media (min-width: 900px) { + flex-direction: row; + align-items: flex-start; + gap: 3rem; + padding-bottom: 2rem; + + .blog-card-image { + flex: 1.5; + border-radius: 4px; + } + + .blog-card-content { + flex: 1; + display: flex; + flex-direction: column; + justify-content: flex-start; + } + + .blog-card-title { + font-size: 2.25rem; + line-height: 1.1; + letter-spacing: -0.02em; + margin-top: 0; + margin-bottom: 0.75rem; + } + + .blog-card-description { + font-size: 1.125rem; + -webkit-line-clamp: 4; + line-clamp: 4; + } + } + } + + &.layout-grid { + @media (min-width: 768px) { + height: 100%; + border: 1px solid var(--site-outline); + border-radius: 12px; + padding: 1rem; + transition: transform 0.2s ease, box-shadow 0.2s ease; + + &:hover { + transform: translateY(-4px); + box-shadow: 0 10px 20px -5px rgba(0, 0, 0, 0.1); + border-color: var(--site-primary-color); + } + + .blog-card-image { + margin-bottom: 1rem; + aspect-ratio: 16/10; + } + + .blog-card-title { + font-size: 1.35rem; + } + + .blog-card-description { + -webkit-line-clamp: 2; + line-clamp: 2; + } + } + } + + &.layout-list { + grid-column: 1 / -1; + + @media (min-width: 768px) { + flex-direction: row; + align-items: flex-start; + gap: 2rem; + + .blog-card-image { + width: 250px; + flex-shrink: 0; + } + } + } +} + +.blog-card-meta { + font-size: 0.875rem; + color: var(--site-base-fgColor-alt); + display: flex; + align-items: center; + gap: 0.25rem 0.75rem; + margin-top: auto; + flex-wrap: wrap; + + .blog-card-authors { + display: flex; + align-items: center; + gap: 0.25rem; + } + + .blog-card-avatar { + width: 28px; + height: 28px; + border-radius: 50%; + object-fit: cover; + background-color: var(--site-outline); + + &:not(:first-child) { + margin-left: -18px; + border: 1px solid var(--site-base-bgColor); + box-sizing: content-box; + } + } + + .author { + font-weight: var(--site-fontWeight-bold); + color: var(--site-base-fgColor); + text-decoration: none; + + .author-name { + white-space: nowrap; + } + + .author-separator { + white-space: normal; + } + } +} + +.post-info { + display: flex; + align-items: start; + gap: 0.25rem; + padding: 1rem 0; + margin: 2rem 0; + color: var(--site-base-fgColor); + + .post-info-main { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 0.25rem 0.75rem; + + @media (max-width: 540px) { + flex-direction: column; + align-items: flex-start; + } + } + + >a { + text-decoration: none; + color: inherit; + display: block; + + &:hover .post-info-name { + color: var(--site-primary-color); + } + } + + .post-info-author { + display: flex; + align-items: center; + gap: 0.75rem; + margin-right: 0.25rem; + + + img { + width: 32px; + height: 32px; + border-radius: 50%; + object-fit: cover; + display: block; + + &:not(:first-child) { + margin-left: -28px; + border: 1px solid var(--site-base-bgColor); + box-sizing: content-box; + } + } + } + + .post-info-name { + margin: 0; + font-size: 1rem; + font-weight: var(--site-fontWeight-bold); + line-height: 1.2; + + a, + span { + color: var(--site-base-fgColor); + font-weight: var(--site-fontWeight-bold); + } + + .post-info-author-name { + white-space: nowrap; + } + + .post-info-author-separator { + white-space: normal; + } + } + + .post-info-meta { + font-size: 0.95rem; + color: var(--site-base-fgColor-alt); + font-weight: var(--site-fontWeight-normal); + white-space: nowrap; + + @media (max-width: 540px) { + &::before { + content: ''; + margin: 0; + } + } + } + + .post-info-actions { + margin-left: auto; + display: flex; + align-items: center; + gap: 0.4rem; + } + + .dropdown { + position: relative; + + .dropdown-content { + right: 0; + top: 100%; + min-width: 180px; + + background-color: var(--site-base-bgColor); + color: var(--site-base-fgColor); + border: var(--site-outline) 1px solid; + box-shadow: none; + + font-family: var(--site-ui-fontFamily); + font-size: 1rem; + + a, + button { + color: var(--site-base-fgColor); + + &:hover { + color: var(--site-primary-color); + } + } + } + + .dropdown-menu { + + svg, + .material-symbols { + width: 20px; + height: 20px; + font-size: 20px; + fill: currentColor; + } + } + } + + + @media (max-width: 600px) { + gap: 0.5rem; + } +} + +.blog-subtitle { + font-size: 1.5rem; + line-height: 1.4; + color: var(--site-base-fgColor-alt); + margin-top: 0; + margin-bottom: 2rem; + font-weight: var(--site-fontWeight-normal); +} + +.post-content { + font-family: var(--site-blog-fontFamily); + line-height: 1.7em; + font-size: 1.2rem; + + h1, + h2, + h3, + h4, + h5, + h6 { + font-family: var(--site-ui-fontFamily); + font-weight: var(--site-fontWeight-bold); + } + + .postbreak span { + background-color: var(--site-base-fgColor); + } +} + +.blog-next-posts { + margin: 4rem 0; + padding-top: 4rem; + border-top: 1px solid var(--site-outline); + + h2 { + font-size: 2rem; + margin-top: 0; + margin-bottom: 2rem; + } + + .blog-posts-grid { + margin-top: 2rem; + } +} \ No newline at end of file diff --git a/packages/site_shared/lib/assets/social/bluesky.svg b/packages/site_shared/lib/assets/social/bluesky.svg new file mode 100644 index 0000000000..f14d84b6a5 --- /dev/null +++ b/packages/site_shared/lib/assets/social/bluesky.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/site_shared/lib/assets/social/github.svg b/packages/site_shared/lib/assets/social/github.svg new file mode 100644 index 0000000000..405d8a4604 --- /dev/null +++ b/packages/site_shared/lib/assets/social/github.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/packages/site_shared/lib/assets/social/linkedin.svg b/packages/site_shared/lib/assets/social/linkedin.svg new file mode 100644 index 0000000000..8c77b11d18 --- /dev/null +++ b/packages/site_shared/lib/assets/social/linkedin.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/packages/site_shared/lib/assets/social/medium.svg b/packages/site_shared/lib/assets/social/medium.svg new file mode 100755 index 0000000000..33b6b60754 --- /dev/null +++ b/packages/site_shared/lib/assets/social/medium.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/packages/site_shared/lib/assets/social/x.svg b/packages/site_shared/lib/assets/social/x.svg new file mode 100644 index 0000000000..b6b96226a8 --- /dev/null +++ b/packages/site_shared/lib/assets/social/x.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/site_shared/lib/blog.dart b/packages/site_shared/lib/blog.dart new file mode 100644 index 0000000000..2e5def88d2 --- /dev/null +++ b/packages/site_shared/lib/blog.dart @@ -0,0 +1,6 @@ +// Copyright 2026 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'src/blog/atom_feed.dart'; +export 'src/blog/models.dart'; diff --git a/packages/site_shared/lib/components/blog/blog_card.dart b/packages/site_shared/lib/components/blog/blog_card.dart new file mode 100644 index 0000000000..48a4cd837f --- /dev/null +++ b/packages/site_shared/lib/components/blog/blog_card.dart @@ -0,0 +1,116 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:jaspr/dom.dart'; +import 'package:jaspr/jaspr.dart'; +import 'package:jaspr_content/jaspr_content.dart'; + +import '../../blog.dart'; +import '../../util.dart'; + +class BlogCard extends StatelessComponent { + const BlogCard({ + required this.post, + required this.url, + this.className, + this.priority = BlogCardPriority.normal, + super.key, + }); + + final Post post; + final String url; + final String? className; + final BlogCardPriority priority; + + @override + Component build(BuildContext context) { + final authors = context.page.authorsByIds(post.authorIds); + return a( + href: url, + classes: ['blog-card', ?className].toClasses, + attributes: {'data-category': post.category ?? 'other'}, + [ + if (post.image case final postImage?) + div(classes: 'blog-card-image', [ + img( + src: postImage, + alt: post.title, + loading: switch (priority) { + .featured => MediaLoading.eager, + .high => null, + .normal => MediaLoading.lazy, + }, + attributes: { + if (priority == .featured) + 'fetchpriority': 'high' + else if (priority == .normal) + 'decoding': 'async', + }, + ), + ]), + div(classes: 'blog-card-content', [ + h3(classes: 'blog-card-title', [ + .text(post.title), + ]), + p(classes: 'blog-card-description', [ + .text(post.description), + ]), + const span(classes: 'blog-card-spacer', []), + div(classes: 'blog-card-meta', [ + div(classes: 'blog-card-authors', [ + for (final author in authors) + if (author.image case final authorImage? + when authorImage.isNotEmpty) + img( + src: context.resolveAsset('/blog/authors/$authorImage'), + alt: author.name, + classes: 'blog-card-avatar', + ) + else if (author.github?.avatarUrl case final avatarUrl?) + img( + src: avatarUrl, + alt: author.name, + classes: 'blog-card-avatar', + ), + span(classes: 'author', [ + for (final (index, author) in authors.indexed) ...[ + span(classes: 'author-name', [ + .text(author.name), + ]), + if (index < authors.length - 1) + const span(classes: 'author-separator', [.text(', ')]), + ], + ]), + ]), + div([ + span(classes: 'date', [ + .text(post.formattedDate), + ]), + const span(classes: 'separator', [.text(' · ')]), + span(classes: 'reading-time', [ + .text(post.readingTime), + ]), + ]), + ]), + ]), + ], + ); + } +} + +/// Priority levels for blog cards. +/// +/// Used to configure loading behavior of their content, particularly images. +enum BlogCardPriority { + /// A featured blog card. + featured, + + /// A high-priority blog card. + /// + /// Often the first 5 or so cards. + high, + + /// A normal-priority blog card. + normal, +} diff --git a/packages/site_shared/lib/components/blog/blog_index.dart b/packages/site_shared/lib/components/blog/blog_index.dart new file mode 100644 index 0000000000..fd8cc84b1d --- /dev/null +++ b/packages/site_shared/lib/components/blog/blog_index.dart @@ -0,0 +1,77 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:collection/collection.dart'; +import 'package:jaspr/dom.dart'; +import 'package:jaspr/jaspr.dart'; +import 'package:jaspr_content/jaspr_content.dart'; + +import '../../blog.dart'; +import '../common/material_icon.dart'; +import 'blog_card.dart'; +import 'client/blog_categories.dart'; + +extension PostIndex on BuildContext { + List<({String url, Post post})> get blogPosts { + final posts = <({String url, Post post})>[ + for (final page in pages) + if (page.url.startsWith('/blog/')) + if (Post.tryParse(page.data.page) case final post?) + (url: page.url, post: post), + ]; + + return posts + .sortedBy((entry) => entry.post.dateObject) + .reversed + .toList(growable: false); + } +} + +class BlogIndex extends StatelessComponent { + const BlogIndex({super.key}); + + @override + Component build(BuildContext context) { + final posts = context.blogPosts; + final categories = context.page.blogCategories; + + return div(classes: 'blog-index', [ + div(classes: 'blog-index-toolbar', [ + BlogCategories(categories: categories), + a( + href: blogAtomFeedPath, + classes: 'blog-feed-link', + attributes: { + 'type': blogAtomMimeType, + 'title': context.page.blogAtomSubscribeTitle, + }, + const [ + MaterialIcon('rss_feed'), + span(classes: 'blog-feed-link-label', [.text('Subscribe')]), + ], + ), + ]), + div( + id: 'blog-container', + classes: 'blog-posts-grid', + attributes: {'data-selected': 'all'}, + [ + for (var i = 0; i < posts.length; i++) + BlogCard( + post: posts[i].post, + url: posts[i].url, + priority: switch (i) { + 0 => BlogCardPriority.featured, + < 5 => BlogCardPriority.high, + _ => BlogCardPriority.normal, + }, + className: i == 0 + ? 'layout-featured' + : (i < 5 ? 'layout-grid' : 'layout-list'), + ), + ], + ), + ]); + } +} diff --git a/packages/site_shared/lib/components/blog/blog_next_posts.dart b/packages/site_shared/lib/components/blog/blog_next_posts.dart new file mode 100644 index 0000000000..6047f2e604 --- /dev/null +++ b/packages/site_shared/lib/components/blog/blog_next_posts.dart @@ -0,0 +1,56 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:jaspr/dom.dart'; +import 'package:jaspr/jaspr.dart'; +import 'package:jaspr_content/jaspr_content.dart'; + +import '../../blog.dart'; +import 'blog_card.dart'; +import 'blog_index.dart'; +import 'client/blog_categories.dart'; + +class BlogNextPosts extends StatelessComponent { + const BlogNextPosts({ + required this.currentPage, + required this.category, + super.key, + }); + + final Page currentPage; + final BlogCategory? category; + + @override + Component build(BuildContext context) { + final nextPosts = context.blogPosts + .where( + (page) => + page.url != currentPage.url && + page.post.category == category?.slug, + ) + .take(2) + .toList(); + + if (nextPosts.isEmpty) return const .fragment([]); + + final String nextPostsTitle; + if (context.page.blogData case {'nextPostsTitle': final String title}) { + nextPostsTitle = title; + } else { + throw Exception('Missing or invalid blog next posts title'); + } + + return section(classes: 'blog-next-posts', [ + h2([.text(nextPostsTitle)]), + div(classes: 'blog-posts-grid', [ + for (final post in nextPosts) + BlogCard( + post: post.post, + url: post.url, + className: 'layout-grid', + ), + ]), + ]); + } +} diff --git a/packages/site_shared/lib/components/blog/client/blog_categories.dart b/packages/site_shared/lib/components/blog/client/blog_categories.dart new file mode 100644 index 0000000000..0c41097f5d --- /dev/null +++ b/packages/site_shared/lib/components/blog/client/blog_categories.dart @@ -0,0 +1,157 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:jaspr/dom.dart'; +import 'package:jaspr/jaspr.dart'; +import 'package:universal_web/web.dart' as web; + +import '../../../util.dart'; + +class BlogCategory { + const BlogCategory({ + required this.slug, + required this.label, + required this.showFeatured, + }); + + final String slug; + final String label; + final bool showFeatured; + + @decoder + factory BlogCategory.fromMap(Map data) { + return BlogCategory( + slug: data['slug'] as String, + label: data['label'] as String, + showFeatured: data['showFeatured'] == true, + ); + } + @encoder + Map toMap() => { + 'slug': slug, + 'label': label, + 'showFeatured': showFeatured, + }; +} + +@client +class BlogCategories extends StatefulComponent { + const BlogCategories({required this.categories, super.key}); + + final List categories; + + @override + State createState() => _BlogCategoriesState(); +} + +class _BlogCategoriesState extends State { + BlogCategory? selectedCategory; + + @override + void initState() { + super.initState(); + if (kIsWeb) { + _updateCategoryFromUrl(); + } + } + + void _updateCategoryFromUrl() { + final uri = Uri.parse(web.window.location.href); + final slug = uri.queryParameters['category']; + final category = component.categories + .where((c) => c.slug == slug) + .firstOrNull; + + setState(() { + selectedCategory = category; + }); + _applyLayout(category); + } + + void _selectCategory(BlogCategory? category) { + if (category == selectedCategory) return; + + setState(() { + selectedCategory = category; + }); + + final uri = Uri.parse(web.window.location.href); + final newUri = uri.replace( + queryParameters: category == null ? {} : {'category': category.slug}, + ); + web.window.history.pushState(null, '', newUri.toString()); + + _applyLayout(category); + } + + void _applyLayout(BlogCategory? category) { + final container = web.document.getElementById('blog-container'); + if (container != null) { + if (category == null) { + container.removeAttribute('data-selected'); + } else { + container.setAttribute('data-selected', category.slug); + } + + final cards = container.querySelectorAll('.blog-card'); + final showFeatured = category?.showFeatured ?? true; + var visibleCount = showFeatured ? 0 : 1; + + for (var i = 0; i < cards.length; i++) { + final card = cards.item(i) as web.Element; + final cardCategory = card.getAttribute('data-category'); + + final isVisible = category == null || cardCategory == category.slug; + + // Remove existing layout classes + card.classList.remove('layout-featured'); + card.classList.remove('layout-grid'); + card.classList.remove('layout-list'); + + if (isVisible) { + if (visibleCount == 0) { + card.classList.add('layout-featured'); + } else if (visibleCount < 5) { + card.classList.add('layout-grid'); + } else { + card.classList.add('layout-list'); + } + visibleCount++; + } + } + } + } + + @override + Component build(BuildContext context) { + return div(classes: 'blog-categories', [ + div( + classes: [ + 'blog-category-chip', + if (selectedCategory == null) 'active', + ].toClasses, + [ + button( + onClick: () => _selectCategory(null), + [const .text('All')], + ), + ], + ), + const span(classes: 'category-separator', []), + for (final category in component.categories) + div( + classes: [ + 'blog-category-chip', + if (category == selectedCategory) 'active', + ].toClasses, + [ + button( + onClick: () => _selectCategory(category), + [.text(category.label)], + ), + ], + ), + ]); + } +} diff --git a/packages/site_shared/lib/components/blog/client/share_button.dart b/packages/site_shared/lib/components/blog/client/share_button.dart new file mode 100644 index 0000000000..0fe4bf326e --- /dev/null +++ b/packages/site_shared/lib/components/blog/client/share_button.dart @@ -0,0 +1,115 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:jaspr/dom.dart'; +import 'package:jaspr/jaspr.dart'; +import 'package:universal_web/web.dart' as web; + +import '../../common/button.dart'; +import '../../common/dropdown.dart'; +import '../../common/material_icon.dart'; + +@client +class ShareButton extends StatelessComponent { + const ShareButton({ + required this.url, + required this.title, + super.key, + }); + + final String url; + final String title; + + @override + Component build(BuildContext context) { + return Dropdown( + id: 'share-menu-${url.hashCode}', + toggle: const Button( + icon: 'share', + title: 'Share this post', + ), + content: div(classes: 'dropdown-menu', [ + ul([ + li([ + Builder( + builder: (context) { + return button( + onClick: () { + web.window.navigator.clipboard.writeText(url); + context.findAncestorStateOfType()?.toggle( + to: false, + ); + }, + [ + const MaterialIcon('content_copy'), + const .text('Copy link'), + ], + ); + }, + ), + ]), + ]), + const div(classes: 'dropdown-divider', []), + ul([ + li([ + a( + href: + 'https://twitter.com/intent/tweet?text=${Uri.encodeComponent(title)}&url=${Uri.encodeComponent(url)}', + target: Target.blank, + [ + const svg([ + Component.element( + tag: 'use', + attributes: { + 'href': '/packages/site_shared/assets/social/x.svg#x', + }, + ), + ]), + const .text('Share on X'), + ], + ), + ]), + li([ + a( + href: + 'https://bsky.app/intent/compose?text=${Uri.encodeComponent("$title $url")}', + target: Target.blank, + [ + const svg([ + Component.element( + tag: 'use', + attributes: { + 'href': + '/packages/site_shared/assets/social/bluesky.svg#bluesky', + }, + ), + ]), + const .text('Share on Bluesky'), + ], + ), + ]), + li([ + a( + href: + 'https://www.linkedin.com/sharing/share-offsite/?url=${Uri.encodeComponent(url)}', + target: Target.blank, + [ + const svg([ + Component.element( + tag: 'use', + attributes: { + 'href': + '/packages/site_shared/assets/social/linkedin.svg#linkedin', + }, + ), + ]), + const .text('Share on LinkedIn'), + ], + ), + ]), + ]), + ]), + ); + } +} diff --git a/packages/site_shared/lib/components/blog/post_info.dart b/packages/site_shared/lib/components/blog/post_info.dart new file mode 100644 index 0000000000..36d166de87 --- /dev/null +++ b/packages/site_shared/lib/components/blog/post_info.dart @@ -0,0 +1,86 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:jaspr/dom.dart'; +import 'package:jaspr/jaspr.dart'; +import 'package:jaspr_content/jaspr_content.dart'; + +import '../../src/blog/atom_feed.dart'; +import '../../src/blog/models.dart'; +import '../common/button.dart'; +import 'client/share_button.dart'; + +class PostInfo extends StatelessComponent { + const PostInfo({ + required this.post, + required this.url, + super.key, + }); + + final Post post; + final String url; + + @override + Component build(BuildContext context) { + final authors = context.page.authorsByIds(post.authorIds); + final siteUrl = context.page.data.site['url']; + + return div(classes: 'post-info', [ + div(classes: 'post-info-main', [ + _AuthorInfo(authors: authors), + span(classes: 'post-info-meta', [ + Component.text('${post.formattedDate} · ${post.readingTime}'), + ]), + ]), + div(classes: 'post-info-actions', [ + Button( + icon: 'rss_feed', + href: blogAtomFeedPath, + title: context.page.blogAtomSubscribeTitle, + attributes: {'type': blogAtomMimeType}, + ), + ShareButton(url: '$siteUrl$url', title: post.title), + ]), + ]); + } +} + +class _AuthorInfo extends StatelessComponent { + const _AuthorInfo({required this.authors}); + + final List authors; + + @override + Component build(BuildContext context) { + return div(classes: 'post-info-author', [ + for (final author in authors) + if (author.image case final authorImage? when authorImage.isNotEmpty) + img( + src: context.resolveAsset('/blog/authors/$authorImage'), + alt: author.name, + width: 32, + height: 32, + ) + else if (author.github?.avatarUrl case final avatarUrl?) + img( + src: avatarUrl, + alt: author.name, + width: 32, + height: 32, + ), + h3(classes: 'post-info-name', [ + for (final (index, author) in authors.indexed) ...[ + span(classes: 'post-info-author-name', [ + if (author.linkUrl case final linkUrl?) + a(href: linkUrl, target: Target.blank, [.text(author.name)]) + else + .text(author.name), + ]), + if (index < authors.length - 1) + const span(classes: 'post-info-author-separator', [.text(', ')]), + ], + ]), + ]); + } +} diff --git a/packages/site_shared/lib/src/blog/atom_feed.dart b/packages/site_shared/lib/src/blog/atom_feed.dart new file mode 100644 index 0000000000..a7fd1cf8b4 --- /dev/null +++ b/packages/site_shared/lib/src/blog/atom_feed.dart @@ -0,0 +1,268 @@ +// Copyright 2026 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:collection/collection.dart'; +import 'package:intl/intl.dart'; +import 'package:jaspr/server.dart'; +import 'package:jaspr_content/jaspr_content.dart'; +import 'package:xml/xml.dart'; + +import 'models.dart'; + +/// The site-relative path the [BlogAtomFeedOutput] publishes the blog feed at. +const String blogAtomFeedPath = '/blog/feed.xml'; + +/// The MIME type of the published Atom feed. +const String blogAtomMimeType = 'application/atom+xml'; + +const String _atomNamespace = 'http://www.w3.org/2005/Atom'; + +final DateFormat _atomDateFormat = DateFormat('yyyy-MM-dd'); +final RegExp _blogIndexPattern = RegExp(r'^blog/index\.(md|html)$'); + +/// A secondary output that generates an Atom feed for the blog. +/// +/// Blog posts are pages beneath `/blog/` with a +/// `publishDate` in their frontmatter. +/// Posts that set `atom: false` are excluded. +final class BlogAtomFeedOutput implements SecondaryOutput { + const BlogAtomFeedOutput(); + + @override + Pattern get pattern => _blogIndexPattern; + + @override + String createRoute(String _) => blogAtomFeedPath; + + @override + Component build(Page page) => Builder( + builder: (context) { + context.setHeader( + 'Content-Type', + '$blogAtomMimeType; charset=utf-8', + ); + context.setStatusCode( + 200, + responseBody: renderAtomFeed(page, context.pages), + ); + return const Component.empty(); + }, + ); + + /// The Atom feed XML rendered from [blogIndex] and + /// the candidate [pages] to include as entries. + @visibleForTesting + String renderAtomFeed(Page blogIndex, List pages) { + final pageData = blogIndex.data; + final siteUrl = pageData.site['url']; + if (siteUrl is! String) { + throw Exception('Site URL not configured in site data.'); + } + + final siteBaseUrl = Uri.parse(siteUrl); + final feedUrl = siteBaseUrl.resolve(blogAtomFeedPath).toString(); + final blogUrl = siteBaseUrl.resolve('/blog').toString(); + final feedTitle = pageData.page['title']; + if (feedTitle is! String) { + throw Exception('Blog index title not configured in page data.'); + } + final feedDescription = pageData.page['description']; + if (feedDescription is! String) { + throw Exception('Blog index description not configured in page data.'); + } + + final String feedAuthor; + if (pageData['blog'] case {'atom': {'feedAuthor': final String author}}) { + feedAuthor = author; + } else { + throw Exception('Blog atom feed author not configured in page data.'); + } + + final (:entries, :feedUpdated) = _entriesFor(siteBaseUrl, pages); + + final builder = XmlBuilder(); + builder.processing('xml', 'version="1.0" encoding="utf-8"'); + builder.element( + 'feed', + namespaces: const {_atomNamespace: ''}, + nest: () { + builder + ..writeTextElement('title', feedTitle) + ..writeTextElement('subtitle', feedDescription) + ..element('id', nest: feedUrl) + ..writeLink( + rel: 'self', + type: blogAtomMimeType, + href: feedUrl, + ) + ..writeLink(rel: 'alternate', type: 'text/html', href: blogUrl) + ..element('updated', nest: feedUpdated.toIso8601String()) + ..writeAuthor(feedAuthor); + + for (final entry in entries) { + entry.writeTo(builder); + } + }, + ); + + return builder.buildDocument().toXmlString(pretty: true, indent: ' '); + } + + /// The entries to write to the feed alongside the + /// latest `updated` timestamp across them. + ({List<_AtomEntry> entries, DateTime feedUpdated}) _entriesFor( + Uri siteBaseUrl, + List pages, + ) { + final entries = <_AtomEntry>[]; + DateTime? feedUpdated; + + for (final page in pages) { + if (!page.url.startsWith('/blog/')) continue; + final pageData = page.data.page; + if (pageData['atom'] == false) continue; + final post = Post.tryParse(pageData); + if (post == null) continue; + + final published = _atomDateFormat.parseStrict(post.publishDate, true); + final updated = switch (pageData['updatedDate']) { + final String updatedDate => _latest( + _atomDateFormat.parseStrict(updatedDate, true), + published, + ), + _ => published, + }; + + entries.add( + _AtomEntry( + url: siteBaseUrl.resolve(page.url).toString(), + title: post.title, + summary: post.description, + authors: [ + for (final author in page.authorsByIds(post.authorIds)) author.name, + ], + category: post.category, + published: published, + updated: updated, + ), + ); + feedUpdated = _latest(feedUpdated, updated); + } + + if (feedUpdated == null) { + print( + 'Warning: No blog posts qualified for the Atom feed. ' + 'Generated an empty feed.', + ); + } + + return ( + entries: entries.sorted(), + feedUpdated: feedUpdated ?? DateTime.utc(1970), + ); + } +} + +/// The rendered representation of a single blog post in the Atom feed. +/// +/// Entries sort by [published] descending, +/// with [url] as a tiebreaker so same-day posts have a stable order. +final class _AtomEntry implements Comparable<_AtomEntry> { + const _AtomEntry({ + required this.url, + required this.title, + required this.summary, + required this.authors, + required this.published, + required this.updated, + this.category, + }); + + /// The absolute URL of the blog post. + final String url; + + /// The post's title, written to ``. + final String title; + + /// The post's description, written to `<summary>`. + final String summary; + + /// The display names of the post's authors. + final List<String> authors; + + /// The time the post was first published. + final DateTime published; + + /// The time the post was last updated, or [published] if never updated. + final DateTime updated; + + /// The post's category slug, + /// written as the `term` of a `<category>` element, + /// or `null` if the post has no category. + final String? category; + + @override + int compareTo(_AtomEntry other) { + final byPublished = other.published.compareTo(published); + if (byPublished != 0) return byPublished; + return url.compareTo(other.url); + } + + /// Writes this entry as a complete Atom `<entry>` element to [builder]. + void writeTo(XmlBuilder builder) { + builder.element( + 'entry', + nest: () { + builder + ..writeTextElement('title', title) + ..writeLink(rel: 'alternate', type: 'text/html', href: url) + ..element('id', nest: url) + ..element('updated', nest: updated.toIso8601String()) + ..element('published', nest: published.toIso8601String()); + for (final author in authors) { + builder.writeAuthor(author); + } + builder.writeTextElement('summary', summary); + if (category case final category?) { + builder.element('category', attributes: {'term': category}); + } + }, + ); + } +} + +extension on XmlBuilder { + /// Writes an Atom text construct (such as `<title>` or `<summary>`) named + /// [name] with [value] as its text content. + void writeTextElement(String name, String value) { + element(name, attributes: const {'type': 'text'}, nest: value); + } + + /// Writes an Atom `<link>` element with the given [rel], [type], and [href]. + void writeLink({ + required String rel, + required String type, + required String href, + }) { + element( + 'link', + attributes: {'rel': rel, 'type': type, 'href': href}, + ); + } + + /// Writes an Atom `<author>` element wrapping a + /// single `<name>` set to [name]. + void writeAuthor(String name) { + element('author', nest: () => element('name', nest: name)); + } +} + +/// The later of [previous] and [candidate], +/// or [candidate] if [previous] is `null`. +DateTime _latest(DateTime? previous, DateTime candidate) { + if (previous == null || candidate.isAfter(previous)) { + return candidate; + } + return previous; +} diff --git a/packages/site_shared/lib/src/blog/models.dart b/packages/site_shared/lib/src/blog/models.dart new file mode 100644 index 0000000000..61539e28da --- /dev/null +++ b/packages/site_shared/lib/src/blog/models.dart @@ -0,0 +1,127 @@ +// Copyright 2026 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:intl/intl.dart'; +import 'package:jaspr_content/jaspr_content.dart'; + +import '../../components/blog/client/blog_categories.dart'; + +extension type Post(Map<String, Object?> data) { + static Post? tryParse(Map<String, Object?> data) { + if (data['title'] is! String || + data['description'] is! String || + data['publishDate'] is! String || + !_isValidAuthorProperty(data['author'])) { + return null; + } + return Post(data); + } + + String get title => data['title'] as String; + String get description => data['description'] as String; + String? get image => data['image'] as String?; + String get publishDate => data['publishDate'] as String; + + DateTime get dateObject => DateTime.parse(publishDate); + String get formattedDate => DateFormat.yMMMd().format(dateObject); + + List<String> get authorIds => switch (data['author']) { + final String authorId => [authorId], + final List<Object?> authorIds => authorIds.cast<String>(), + _ => throw ArgumentError( + 'Post author is neither a string nor a list of strings for "$title".', + ), + }; + + String get readingTime => data['readingTime'] as String? ?? '5 min read'; + String? get category => data['category'] as String?; +} + +extension type Author(Map<String, Object?> data) { + String get name => data['name'] as String; + String? get bio => data['bio'] as String?; + String? get image => data['image'] as String?; + String? get twitter => data['twitter'] as String?; + AuthorGithub? get github => data['github'] as AuthorGithub?; + + String? get linkUrl { + if (github?.handle case final githubHandle? when githubHandle.isNotEmpty) { + return 'https://github.com/$githubHandle'; + } + if (twitter case final twitterHandle? when twitterHandle.isNotEmpty) { + return 'https://twitter.com/$twitterHandle'; + } + return null; + } +} + +extension type AuthorGithub(Map<String, Object?> data) { + String get handle => data['handle'] as String; + String? get name => data['username'] as String?; + String? get avatarUrl => data['avatar_url'] as String?; +} + +extension BlogData on Page { + Map<String, Object?> get blogData { + if (data['blog'] case {'data': final Map<String, Object?> data}) { + return data; + } else { + throw ArgumentError('Missing or invalid blog data.'); + } + } + + List<BlogCategory> get blogCategories { + final categories = blogData['categories']; + if (categories is! List<Object?>) { + throw ArgumentError('Missing or invalid blog categories'); + } + return categories + .map((c) => BlogCategory.fromMap(c as Map<String, Object?>)) + .toList(); + } + + String get blogAtomSubscribeTitle { + if (blogData case {'atom': {'subscribeTitle': final String title}}) { + return title; + } else { + throw Exception('Missing or invalid blog atom subscribe title.'); + } + } + + /// Returns the author with the specified [id]. + Author authorById(String id) { + final authors = (data['blog'] as Map<String, Object?>)['authors']; + if (authors is! Map<String, Object?>) { + throw Exception('Authors data not found or invalid.'); + } + + final author = authors[id]; + if (author == null) { + throw Exception('Author not found: $id'); + } + if (author is! Map<String, Object?>) { + throw Exception('Author data not found or invalid: $id'); + } + return Author(author); + } + + /// Returns the authors with the specified [ids]. + List<Author> authorsByIds(List<String> ids) => [ + for (final id in ids) authorById(id), + ]; +} + +/// Whether [author] is a valid author configuration: +/// either a single author ID or a non-empty list of author IDs. +bool _isValidAuthorProperty(Object? author) { + return switch (author) { + final String authorId => authorId.isNotEmpty, + final List<Object?> authorIds => + authorIds.isNotEmpty && + authorIds.every( + (authorId) => authorId is String && authorId.isNotEmpty, + ), + _ => false, + }; +} diff --git a/packages/site_shared/lib/util.dart b/packages/site_shared/lib/util.dart index e064a2c721..26eea29d14 100644 --- a/packages/site_shared/lib/util.dart +++ b/packages/site_shared/lib/util.dart @@ -2,11 +2,36 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:jaspr/jaspr.dart'; + export 'src/utils/slugify.dart'; /// Whether this build of the site will be deployed to production. const bool productionBuild = .fromEnvironment('PRODUCTION'); +/// Split the specific [sourceString] into a list of Jaspr [Component] +/// by adding a `<wbr>` element after each underscore. +/// +/// This is useful for long IDs separated with underscores, such as lint names, +/// that might otherwise break across lines in an undesirable way. +List<Component> splitByUnderscore(String sourceString) { + final parts = sourceString.split('_'); + final result = <Component>[]; + + for (var i = 0; i < parts.length; i++) { + result.add(.text(parts[i])); + + // Add a word break opportunity after each underscore, + // except for the final one. + if (i < parts.length - 1) { + result.add(const Component.text('_')); + result.add(const Component.element(tag: 'wbr')); + } + } + + return result; +} + /// Parses attribute tokens from [attributeString] into HTML attributes. /// /// Supports IDs (`#id`), classes (`.class`), and `key="value"` pairs, diff --git a/packages/site_shared/pubspec.yaml b/packages/site_shared/pubspec.yaml index acc16fe545..78727ab396 100644 --- a/packages/site_shared/pubspec.yaml +++ b/packages/site_shared/pubspec.yaml @@ -10,6 +10,7 @@ dependencies: collection: ^1.19.1 crypto: ^3.0.7 html: ^0.15.6 + intl: ^0.20.2 jaspr: ^0.23.1 jaspr_content: ^0.5.2 markdown: ^7.3.1 @@ -19,6 +20,7 @@ dependencies: opal: ^0.2.2 path: ^1.9.1 universal_web: ^1.1.1+1 + xml: ^6.6.1 yaml: ^3.1.3 dev_dependencies: diff --git a/pubspec.yaml b/pubspec.yaml index 51ff774796..8e24e5a853 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,6 @@ name: flutter_websites description: >- Pub workspace for Flutter websites and related tooling. publish_to: none - environment: sdk: ^3.12.0 diff --git a/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/10WJQz_1nMC95larhInedMg.webp b/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/10WJQz_1nMC95larhInedMg.webp new file mode 100644 index 0000000000..c591e53396 Binary files /dev/null and b/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/10WJQz_1nMC95larhInedMg.webp differ diff --git a/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/16I979gEcIqfLnPdshiXjRQ.jpeg b/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/16I979gEcIqfLnPdshiXjRQ.jpeg new file mode 100644 index 0000000000..cbabede9e6 Binary files /dev/null and b/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/16I979gEcIqfLnPdshiXjRQ.jpeg differ diff --git a/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/18qm-mMi6Hod2jBw1_2nNEw.webp b/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/18qm-mMi6Hod2jBw1_2nNEw.webp new file mode 100644 index 0000000000..d8c971994a Binary files /dev/null and b/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/18qm-mMi6Hod2jBw1_2nNEw.webp differ diff --git a/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/1JLGrYXO8VOHh2Qp1NeHykQ.webp b/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/1JLGrYXO8VOHh2Qp1NeHykQ.webp new file mode 100644 index 0000000000..0d7879e511 Binary files /dev/null and b/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/1JLGrYXO8VOHh2Qp1NeHykQ.webp differ diff --git a/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/1Nrox9tKM1Jok5MQWp4q4Og.webp b/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/1Nrox9tKM1Jok5MQWp4q4Og.webp new file mode 100644 index 0000000000..2293230a3f Binary files /dev/null and b/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/1Nrox9tKM1Jok5MQWp4q4Og.webp differ diff --git a/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/1fCJrAXkiAh4_FNu5I7L_bQ.webp b/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/1fCJrAXkiAh4_FNu5I7L_bQ.webp new file mode 100644 index 0000000000..306f4316cf Binary files /dev/null and b/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/1fCJrAXkiAh4_FNu5I7L_bQ.webp differ diff --git a/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/1sV9X0IeCaF-IInvjLa4Ucg.webp b/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/1sV9X0IeCaF-IInvjLa4Ucg.webp new file mode 100644 index 0000000000..f7ee087098 Binary files /dev/null and b/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/1sV9X0IeCaF-IInvjLa4Ucg.webp differ diff --git a/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/1wJetTyHJ1UDBBdMXq8GfIg.webp b/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/1wJetTyHJ1UDBBdMXq8GfIg.webp new file mode 100644 index 0000000000..144f0d0789 Binary files /dev/null and b/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/images/1wJetTyHJ1UDBBdMXq8GfIg.webp differ diff --git a/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/index.md b/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/index.md new file mode 100644 index 0000000000..b7cc027693 --- /dev/null +++ b/sites/www/content/blog/20-apps-in-20-days-with-flutter-and-antigravity/index.md @@ -0,0 +1,136 @@ +--- +title: "20 apps in 20 days with Flutter and Antigravity" +description: "Why I stopped planning and started building" +publishDate: 2026-03-05 +author: lamek +image: images/18qm-mMi6Hod2jBw1_2nNEw.webp +layout: blog +--- + +<DashImage figure src="images/18qm-mMi6Hod2jBw1_2nNEw.webp" alt="Dash having fun building Flutter apps." caption="Dash having fun building Flutter apps." /> + + +Hi, I’m Kevin Lamenzo! I recently joined the Dart and Flutter teams (🎉), and this January I set out on a quest: build 20 apps in 20 days using Flutter and [Antigravity](https://antigravity.google/). But why? First, the cost of curiosity has officially hit zero. You can go from idea to app in 10 minutes. Second, as a new member of the team I wanted to learn about the framework by building something. Last, and most importantly, I wanted to test the theory that in 2026, we all can be builders now. + +I’m here to share what I learned from the trenches. If you take only one thing from this post, let it be this: stop reading, and go build something. + +## The spark + +My first app was a health tracker. + +During a recent check-up, my physician “strongly suggested” that I watch my blood pressure and alcohol intake. My first reaction? “Don’t tell me what to do!” My second reaction? “I’m gonna build my own app for that.” No subscription offers, no data harvesting, no gamified onboarding. Just a simple tool that solved my specific problem. + +<DashImage figure src="images/1sV9X0IeCaF-IInvjLa4Ucg.webp" alt="My first app — a personal health tracker." caption="My first app — a personal health tracker." /> + + +## Why Flutter? + +I’m always on the go. I initially built a web app, which is nice, but I needed this tool to be in my pocket. Flutter makes the transition from web app to mobile app feel like magic. Antigravity barely needed to change the code. + +After building the mobile version, I even opened a [Google Play Developer](https://play.google.com/console/) account ($25), and released the app to myself as a tester. Now my creation is live on my own phone (although I still don’t have a logo or icon). + +<DashImage figure src="images/10WJQz_1nMC95larhInedMg.webp" alt="The health tracker app, with a default icon, on my phone." caption="The health tracker app, with a default icon, on my phone." /> + + +## Tearing through ideas + +The success of building the health tracker was intoxicating. By the end of the first week, I had four more apps on my phone and I had launched an internal blog (I called it “App a Day”) to document my journey and share the messiness with my colleagues. + +This felt like more than vibecoding — I was building. I was exploring my phone’s sensors, working with haptics, and even using APIs. + +<DashImage figure src="images/1Nrox9tKM1Jok5MQWp4q4Og.webp" alt="My internal blog landing page — App a Day" caption="My internal blog landing page — App a Day" /> + + +## Hitting a wall trying to scale + +Everything wasn’t rosy. When I tried to take one of my quick idea apps and add to it every day of the week, I hit a wall. + +Large scale apps are possible, but you need a different mindset. You have to lean into the architecture. You likely have to ask the agent 100 follow up questions. The more knowledge you bring to this step of the flow, the better an experience you will have. This is your excuse [to go learn “traditional” development](https://docs.flutter.dev/learn/pathway). + +However, the beauty of the “App a Day” mentality is that you don’t always have to scale. Small apps are fast to build, helpful, and you can wake up tomorrow and move to a completely new one. + +This leads to my “AI thought leadership soapbox moment”: +> Right now we’re all empowered to do amazing things alone, but the next frontier is **collaboration**. How we use our AI superpowers to work together in new ways is uncharted territory. So, if you go out and “vibe build” an app with your friends or a team, chart your journey and share it with the rest of us. + +<DashImage figure src="images/1fCJrAXkiAh4_FNu5I7L_bQ.webp" alt="Dash making a point on his soapbox." caption="Dash making a point on his soapbox." /> + + +## My recommended flow + +I got some questions from colleagues and friends about how I was staying organized. Here’s my flow to building your own app: + +1. Get organized ([Google Docs](https://docs.google.com/)) - I haven’t gone fully AI yet. I need something static, something familiar, a place to park my ideas. I started every app, large or small, with a Google doc. I added the date and my quick notes. This gave me a place to return to as needed. + +1. Refine my ideas ([Gemini](https://gemini.google.com/)) - This step is becoming a classic “AI hack”: don’t write your own prompts; write down your raw thoughts and ask an LLM to create a golden prompt for you. + +1. Build! ([Antigravity](https://antigravity.google/)) - Google’s new AI enabled IDE is a workhorse. By comparison, if you ask any of the popular LLMs (Gemini, ChatGPT, etc.) to “write me a book”, they can only output so much. When you ask Antigravity a complex task like this, first it makes a plan, then it works sequentially through each task in that plan. You guide it along the way. I put all of my golden prompts into Antigravity and guided it to build them into apps. + +1. Test, iterate, repeat (also [Antigravity](https://antigravity.google/)) - After Antigravity finishes your first build, it’s time to get hands on. Run the software. Try it out. Provide your feedback to Antigravity, and ask it to make the changes you need. Don’t know how to run a Flutter app? Just ask the Agent in Antigravity. + +1. (optional) Deploy (Google Play, [Firebase](https://firebase.google.com/) - I’ve already mentioned getting my apps on my phone. Another great avenue for sharing is Firebase. Not sure where to start? Ask Gemini to guide you. Firebase makes hosting your apps and adding more advanced services (like authentication, for example) much easier. + +<DashImage figure src="images/16I979gEcIqfLnPdshiXjRQ.jpeg" /> + + +## Breaking out of my internal blog + +The energy I got from this challenge eventually helped me explode right out of it. Instead of an internal blog, I went and launched my own personal site. Here I’m working outside the artificial bounds of building one small thing a day. Instead, I’m working on things big and small, grabbing ideas as they come, and moving on without hesitation. + +Come find me at [ladevzo.com/scrappy-path](http://ladevzo.com/scrappy-path) if you want to see how I’m applying these lessons outside the corporate firewall. + +<DashImage figure src="images/1JLGrYXO8VOHh2Qp1NeHykQ.webp" alt="A screenshot of ladevzo.com. Check it out!" caption="A screenshot of ladevzo.com. Check it out!" /> + + +## Conclusion: find your own flow + +With that said, let me conclude with this: you don’t need to build 20 apps in 20 days. Start small. Use Gemini for brainstorming. Use Antigravity to build a prototype. Before you get bogged down mastering the code, focus on transitioning from “idea” to “working reality”. + +The tools are ready. Are you? + +## Appendix: list of apps + +1. Synonym slider (1 file, 207 LOC) — A simple app to provide the user with a list of synonyms for a given word. Demonstrates basic HTTP integration. + +1. Sensor app (1 file, 228 LOC) — Interacts with device hardware using the sensors plus package. + +1. Sino shift (1 file, 230 LOC) — A language experiment to translate English phrases and sentences into a Chinese-language-style ideograph format (Chinese languages use far fewer tokens when interacting with LLMs by virtue of their syntax) + +1. Vip badge (1 file, 273 LOC) — Implements authentication through Firebase and Google Sign-In. + +1. My apps widget (4 files, 305 LOC) — An Android homescreen “container” to hold all of my apps. Utilizes Android Intents to create a home screen widget. + +1. Haptic soundboard (2 files, 311 LOC) — A soundboard application featuring audio playback capabilities. + +1. Rusty-haiku (4 files, 403 LOC) — A simple haiku generator. Demonstrates API usage and state management with Riverpod. + +1. Thought spot (5 files, 481 LOC) — Place a marker on a map and capture (by voice input) a thought. Features speech-to-text, maps, geolocation, and local databases. + +1. Jolt (6 files, 494 LOC) — Focuses on local storage with shared preferences and custom UI fonts. + +1. Accept changes (2 files, 547 LOC) — An attempt to bounce Antigravity’s push notifications to my phone. Provides device feedback via notification and connects to a Realtime Database. + +1. Street slueth (9 files, 574 LOC) — A spin on Geoguesser: a collaborative murder mystery game where you need to use Google Maps to find clues and solve a puzzle. Map-based application with user authentication and Google Street View integration. + +1. Learn witt (7 files, 588 LOC) — A learning app for understanding Wittgenstein. Focused on user interface design using custom fonts. + +1. Parcheesi game (5 files, 625 LOC) — A Parcheesi remake. This was an attempt at “once shot prompting” with Antigrabity. Manages complex logic and state using Riverpod and Equatable. + +1. Health tracker (6 files, 641 LOC) — Personal health tracking app for monitoring daily blood pressure and alcohol intake. Manages local state and utilizes the file system for storage. + +1. Magic octo (6 files, 692 LOC) — My take on the magic 8-ball. + +1. Meeting helper (5 files, 778 LOC) — Productivity tool using Firebase authentication and Cloud Firestore. + +1. Haircut log (6 files, 901 LOC) — A Google Photos integration that helps hair salons manage user haircut photos leveraging the Google Photos Library API. + +1. Wwks (6 files, 1121 LOC) — What would Kevin say? My personalized, AI-enabled, chat interface. Combines Google Generative AI with a full Firebase backend. + +1. Human speed (15 files, 1124 LOC) — A personal thinking tool. Allows you to self-manage context across LLM threads. Full-stack AI application structured with Riverpod, GoRouter, and Firebase. + +1. Math facts AI(11 files, 1520 LOC) — Educational tool leveraging generative AI to teach math facts. + +1. Workout buddy (11 files, 1582 LOC) — Workout tracker, featuring Riverpod, Cloud Functions, and Freezed code generation. + +1. Pulse (14 files, 1928 LOC) — Large-scale project integrating generative AI, robust state management, and code generation. + +<DashImage figure src="images/1wJetTyHJ1UDBBdMXq8GfIg.webp" alt="Dash getting fit on the treadmill." caption="Dash getting fit on the treadmill." /> diff --git a/sites/www/content/blog/2023-google-mobile-ads-updates-for-flutter/images/1AwHtPUcFDv1jToZIKjye_w.webp b/sites/www/content/blog/2023-google-mobile-ads-updates-for-flutter/images/1AwHtPUcFDv1jToZIKjye_w.webp new file mode 100644 index 0000000000..e5272228ab Binary files /dev/null and b/sites/www/content/blog/2023-google-mobile-ads-updates-for-flutter/images/1AwHtPUcFDv1jToZIKjye_w.webp differ diff --git a/sites/www/content/blog/2023-google-mobile-ads-updates-for-flutter/images/1CkTEpg9fxmW7j_UxQX481A.webp b/sites/www/content/blog/2023-google-mobile-ads-updates-for-flutter/images/1CkTEpg9fxmW7j_UxQX481A.webp new file mode 100644 index 0000000000..602511ef38 Binary files /dev/null and b/sites/www/content/blog/2023-google-mobile-ads-updates-for-flutter/images/1CkTEpg9fxmW7j_UxQX481A.webp differ diff --git a/sites/www/content/blog/2023-google-mobile-ads-updates-for-flutter/images/1PLQQUPqp_e0LmXTbAgb_iQ.webp b/sites/www/content/blog/2023-google-mobile-ads-updates-for-flutter/images/1PLQQUPqp_e0LmXTbAgb_iQ.webp new file mode 100644 index 0000000000..f36441b2c8 Binary files /dev/null and b/sites/www/content/blog/2023-google-mobile-ads-updates-for-flutter/images/1PLQQUPqp_e0LmXTbAgb_iQ.webp differ diff --git a/sites/www/content/blog/2023-google-mobile-ads-updates-for-flutter/images/1Vj2zT8BlnstyfZmvgSXuSw.webp b/sites/www/content/blog/2023-google-mobile-ads-updates-for-flutter/images/1Vj2zT8BlnstyfZmvgSXuSw.webp new file mode 100644 index 0000000000..ebb1ba494d Binary files /dev/null and b/sites/www/content/blog/2023-google-mobile-ads-updates-for-flutter/images/1Vj2zT8BlnstyfZmvgSXuSw.webp differ diff --git a/sites/www/content/blog/2023-google-mobile-ads-updates-for-flutter/index.md b/sites/www/content/blog/2023-google-mobile-ads-updates-for-flutter/index.md new file mode 100644 index 0000000000..e830b5b907 --- /dev/null +++ b/sites/www/content/blog/2023-google-mobile-ads-updates-for-flutter/index.md @@ -0,0 +1,104 @@ +--- +title: "2023 Google Mobile Ads Updates for Flutter" +description: "We added new GMA Flutter features based on your feedback!" +publishDate: 2023-05-10 +author: timothyhoang +image: images/1PLQQUPqp_e0LmXTbAgb_iQ.webp +category: announcements +layout: blog +--- + +<DashImage figure src="images/1PLQQUPqp_e0LmXTbAgb_iQ.webp" /> + + +As you build and grow your Flutter apps, you may want your apps to make money. You can monetize appin many ways. Introducing advertising would be one of the most common strategies. To introduce ads, you can use the [Google Mobile Ads](https://pub.dev/packages/google_mobile_ads) (GMA) Flutter plugin. The GMA plugin can place ads from AdMob, Google Ad Manager, or AdSense in your Flutter mobile app. + +Feature and tooling requests have grown in step with GMA Flutter plugin usage. Based on your Ad Manager and AdMob survey responses, the plugin now supports: + +* Dart support for Native Ads + +* Updated application samples + +* WebView API for Ads + +## Dart support for Native ads + +With Native ads, you can customize the look and feel of the ads that appear in your app. You design the ads from the ground up. Configure how they look, where they’re placed, and how they work within your existing app design. + +Native ads can match the layout of the surrounding content. This results in a more cohesive, natural app experience. You can implement these ads in a number of ways. Add them in your feeds, on transition screens and overlays, or anywhere else you want ads within your app. + +<DashImage figure src="images/1CkTEpg9fxmW7j_UxQX481A.webp" /> + + +Before, implementing Native ads required writing platform code. We agree this was a suboptimal developer experience and want to enable you to use Dart whenever possible. + +We added Dart APIs to enable you to customize your Native ads using Dart. These APIs leverage GMA [Native Templates in Dart](https://developers.google.com/admob/flutter/native/templates) to accelerate the creation of your ad experiences. The new Dart APIs don’t replace platform code. If you want more control over platform-specific ad customization, you can continue to write platform code. + +To learn more about best practices with building Native ad experiences, check out our [Native ads playbook](https://storage.googleapis.com/gweb-dblclk-native-gallery.appspot.com/NativePlaybook.pdf). + +To get started with implementing these ads, check out our [developer documentation](https://developers.google.com/admob/flutter/native). + +## Updated application samples + +When you submit issues on the GMA Flutter plugin, it helps if you include a minimum reproducible app sample to investigate the issue. That said, we heard your feedback that you might have difficulty generating or sharing reproducible samples. + +To simplify troubleshooting issues, we cut our big sample into smaller samples. We replaced our monolithic app sample with [samples for each ad format and mediation](https://github.com/googleads/googleads-mobile-flutter/tree/main/samples/admob). When you submit issues now, you can use these apps. These apps allow you to share a minimum reproducible sample. This helps when sharing a sample of your own app would be cumbersome or impossible. + +<DashImage figure src="images/1AwHtPUcFDv1jToZIKjye_w.webp" /> + + +We hope that these smaller individual app samples simplify testing out new formats or mediation. Each sample includes a readme that supplements our GMA developer documentation ([Ad Manager](https://developers.google.com/ad-manager/mobile-ads-sdk/flutter/quick-start), [AdMob](https://developers.google.com/admob/flutter/quick-start)). + +Each sample app implements two important features: + +1. Google test ads for [Android](https://developers.google.com/admob/android/test-ads) and [iOS](https://developers.google.com/admob/ios/test-ads) + +1. A test application ID. + +When testing functionality of your own ad units in the application samples, replace the ad unit IDs and app IDs with your own. Make sure you have [enabled test ads](https://developers.google.com/ad-manager/mobile-ads-sdk/flutter/test-ads). If you click on too many ads without being in test mode, you risk your account being flagged for invalid activity. + +To illustrate with the [banner app sample](https://github.com/googleads/googleads-mobile-flutter/blob/main/samples/admob/banner_example/lib/main.dart#L25): + +1. Replace the following test ad unit IDs with your own: + +```rust +final String _adUnitId = Platform.isAndroid +? 'ca-app-pub-3940256099942544/6300978111' +: 'ca-app-pub-3940256099942544/2934735716'; +``` + + +2. Replace the following test application IDs in the [Android](https://github.com/googleads/googleads-mobile-flutter/blob/main/samples/admob/banner_example/android/app/src/main/AndroidManifest.xml#L30) and [iOS](https://github.com/googleads/googleads-mobile-flutter/blob/main/samples/admob/banner_example/ios/Runner/Info.plist#L51) config files with your own. + +That’s all it takes to start testing functionality of your + +To view all samples, check out [googleads-mobile-flutter/samples](https://github.com/googleads/googleads-mobile-flutter/tree/main/samples/admob). + +## WebView API for Ads + +We understand some developers prefer using WebViews in their mobile apps. This allows them to avoid the need to re-tag their ad inventory. As stated in our [AdSense policy](https://support.google.com/adsense/answer/48182?sjid=16092961064586512819-NA#trs) and [Ad Manager policy](https://support.google.com/admanager/answer/6310245?sjid=16092961064586512819-NA), we only supported monetization of web content in certain WebView technologies. + +To support Ad Manager and AdSense monetization of mobile app WebViews, we’re introducing Flutter support for the GMA WebView API for Ads. If your app uses WebViews to display web content that serves ads from Ad Manager or AdSense, you must use the WebView API for Ads. + +<DashImage figure src="images/1Vj2zT8BlnstyfZmvgSXuSw.webp" /> + + +1. If you don’t need to request ads from an in-app WebView, implement the ad format using the GMA Flutter plugin ([Ad Manager guide](https://support.google.com/admanager/answer/6238696), [AdMob guide](https://support.google.com/admob/answer/6128738)) + +1. If you request ads from an in-app WebView, use the WebView API for Ads to register the WebView to follow our [AdSense policy](https://support.google.com/adsense/answer/48182?sjid=16092961064586512819-NA#trs) and [Ad Manager policy](https://support.google.com/admanager/answer/6310245?sjid=16092961064586512819-NA). + +You can do either option, or even both, in the same app. + +**Note**: The JavaScript in the [AdSense code](https://support.google.com/adsense/answer/9274634) or [Google Publisher Tag](https://support.google.com/admanager/answer/181073) builds and sends ad requests. You don’t need to make any ad requests with the SDK. Only the mobile web and desktop web [inventory formats](https://support.google.com/admanager/answer/9796545) may be used with this API. + +To start implementing, check out our [developer documentation](https://developers.google.com/admob/flutter/webview). + +## How do we determine what to build next? + +Your feedback influences our product roadmap. + +Since the open beta in 2020, we’ve been working with you in GitHub to troubleshoot issues and collect feature requests. Adoption of the GMA Flutter plugin has seen significant growth. We want to scale our support to meet this higher demand. + +Starting in Q3 2023, if you report a bug or feature request via GitHub Issues, we recommend you visit our [developers forum](https://groups.google.com/group/google-admob-ads-sdk/) for your issue. The developers forum provides your best technical support option. We staff the forum with a technical support team. Developers who use the GMA SDK participate in the forum as well. If you have a question about the Flutter GMA SDK, the forum should provide the fastest and most accurate responses. We’re scaling support for Flutter in the forums to the same teams that support the Android, iOS, and Unity GMA plugins. + +We welcome Flutter developers to the Google Mobile Ads community. We invite you to try the GMA SDK and consult with your fellow app publishers in the forum! \ No newline at end of file diff --git a/sites/www/content/blog/a-roundup-of-flutter-news-at-google-i-o/images/13XedGb879lTK2vbCP0uscA.webp b/sites/www/content/blog/a-roundup-of-flutter-news-at-google-i-o/images/13XedGb879lTK2vbCP0uscA.webp new file mode 100644 index 0000000000..2b9a28568f Binary files /dev/null and b/sites/www/content/blog/a-roundup-of-flutter-news-at-google-i-o/images/13XedGb879lTK2vbCP0uscA.webp differ diff --git a/sites/www/content/blog/a-roundup-of-flutter-news-at-google-i-o/images/1iFS8mhfeoLHEBSfh0p7CTw.webp b/sites/www/content/blog/a-roundup-of-flutter-news-at-google-i-o/images/1iFS8mhfeoLHEBSfh0p7CTw.webp new file mode 100644 index 0000000000..03b6c83ef5 Binary files /dev/null and b/sites/www/content/blog/a-roundup-of-flutter-news-at-google-i-o/images/1iFS8mhfeoLHEBSfh0p7CTw.webp differ diff --git a/sites/www/content/blog/a-roundup-of-flutter-news-at-google-i-o/index.md b/sites/www/content/blog/a-roundup-of-flutter-news-at-google-i-o/index.md new file mode 100644 index 0000000000..02cc543db1 --- /dev/null +++ b/sites/www/content/blog/a-roundup-of-flutter-news-at-google-i-o/index.md @@ -0,0 +1,101 @@ +--- +title: "A roundup of Flutter news at Google I/O" +description: "Flutter: beautiful apps for mobile, web and desktop" +publishDate: 2019-05-08 +author: timsneath +image: images/13XedGb879lTK2vbCP0uscA.webp +category: events +layout: blog +--- + +We shipped an almost overwhelming amount of news about Flutter at [I/O ’19](https://events.google.com/io/) this year, and if it’s not your full-time job to track it all, you might be forgiven for missing some of it. So here’s an aggregation of all the big news, as well as highlights from the press coverage. + +<DashImage figure src="images/13XedGb879lTK2vbCP0uscA.webp" alt="Flutter for Web technical preview, announced by Adam Seligman during the developer keynote at Google I/O ‘19" caption="Flutter for Web technical preview, announced by Adam Seligman during the developer keynote at Google I/O ‘19" /> + + +### TL;DR + +Flutter expands from mobile devices to support the web and desktop: same codebase, same focus on fast, beautiful experiences with high productivity. + +### News and announcements + +* Flutter [now supports the web](https://flutter.dev/web). Open source, [repo on GitHub](https://github.com/flutter/flutter_web). + +* Flutter 1.5 ships to the stable channel ([release notes](https://github.com/flutter/flutter/wiki/Release-Notes-Flutter-1.5.4)). Includes preliminary support for [targeting Windows, Mac and Linux operating systems](https://github.com/flutter/flutter/wiki/Desktop-shells). New plug-ins for [in-app payment](https://pub.dev/packages/in_app_purchase), [state management](https://pub.dev/packages/provider). New samples for [ML Kit](https://github.com/firebase/mlkit-custom-image-classifier/tree/master/flutter-app)-based image classification. + +* Support for [developing on Chrome OS](https://github.com/flutter/flutter/wiki/Linting-Flutter-apps-for-Chrome-OS) and [publishing apps to Chrome OS](https://github.com/flutter/flutter/wiki/Linting-Flutter-apps-for-Chrome-OS). + +* Dart 2.3 [released](https://dart.dev/tools/sdk/archive) with new support for UI-as-code features including the *spread* operator, collection *if* and collection *for*; [website](https://dart.dev) and [package site](https://pub.dev) overhauled. + +* New reference customers for Flutter announced: eBay, Sonos, and New York Times. The Assistant team is using Flutter for their smart display platform with Flutter, powering the UI of devices such as Google Nest Hub. + +* Updates for the [Visual Studio Code](https://dartcode.org/releases/v3-0/) and [Android Studio](https://groups.google.com/d/msg/flutter-dev/qZNjCI_2BLE/_R4iJPOFAAAJ) tooling extensions. + +* [Flutter training course published by App Brewery](https://www.appbrewery.co/p/flutter-development-bootcamp-with-dart/), in collaboration with Google. Thirty hours of videos and labs, at a subsidized price of just $10. + +* Flutter Create award winners [announced](https://medium.com/flutter-io/the-flutter-create-winners-are-40980f2d20b3), along with [demo reel](https://www.youtube.com/watch?v=WLvpROMUjYQ). + +* International [community-organized Flutter hackathon](https://flutterhackathon.com) on June 1st. + +### Articles from the Flutter and Dart teams + +* [Flutter: a portable UI framework for mobile, web, embedded, and desktop](https://developers.googleblog.com/2019/05/Flutter-io19.html) + +* [Bringing Flutter to the web](https://medium.com/flutter-io/bringing-flutter-to-the-web-904de05f0df0) + +* [Announcing Flutter 1.5](https://medium.com/flutter-io/announcing-flutter-1-5-d203c6072e5c) + +* [Dart 2.3: optimized for building user interfaces](https://medium.com/dartlang/announcing-dart-2-3-optimized-for-building-user-interfaces-e84919ca1dff) + +* [Chrome OS and Flutter: better together](https://medium.com/googleplaydev/flutter-and-chrome-os-better-together-14b257ed6776) + +* [Performance testing of Flutter apps](https://medium.com/flutter-io/performance-testing-of-flutter-apps-df7669bb7df7) + +### Articles from partner teams + +* [Flutter is all the things](https://medium.com/flutter-nyc/flutter-is-all-the-things-805caf2793f3) (Very Good Ventures) + +* [Under the Hood with Flutter for Web](https://medium.com/flutter-nyc/under-the-hood-with-flutter-for-web-bc0d5ce1c11e) (Very Good Ventures) + +* [Flutter Developer Quest game at I/O ’19](https://medium.com/2dimensions/flutter-developer-quest-game-at-google-i-o-2019-53971a56a524) (2Dimensions) + +### Flutter and Dart keynote and session recordings + +* [Keynote: announcing the technical preview of Flutter for web](https://twitter.com/googledevs/status/1126222981452247040) + +* [Beyond mobile: Material Design, adaptable UIs and Flutter](https://www.youtube.com/watch?v=YSULAJf6R6M) + +* [Building for iOS with Flutter](https://www.youtube.com/watch?v=ZBJa-xjZl3w) + +* [Building Flutter apps for iOS, Android, Chrome OS and web](https://www.youtube.com/watch?v=IyFZznAk69U) + +* [Dart: Productive, fast and multiplatform — pick 3](https://www.youtube.com/watch?v=J5DQRPRBiFI) + +* [Pragmatic state management with Flutter](https://www.youtube.com/watch?v=d_m5csmrf7I) + +### Demos and Codelabs + +* [New York Times KENKEN game, built with Flutter for web](https://www.nytimes.com/games/prototype/kenken) + +* [Flutter web sample gallery](https://flutter.github.io/samples/) + +* [2Dimensions: Developer Quest game](http://github.com/2d-inc/developer_quest) ([iOS](https://itunes.apple.com/app/id1455994402), [Android](https://play.google.com/store/apps/details?id=dev.flutter.devRpg)) + +* Three new codelabs for Flutter: [Photos API](https://codelabs.developers.google.com/codelabs/google-photos-sharing/index.html), [Google Maps](https://codelabs.developers.google.com/codelabs/google-maps-in-flutter/index.html), [iOS apps](https://codelabs.developers.google.com/codelabs/flutter-cupertino/index.html) + +<DashImage figure src="images/1iFS8mhfeoLHEBSfh0p7CTw.webp" alt="The New York Times KENKEN game, shown running on Chrome, macOS, mobile Safari, iPhone and Android." caption="The New York Times KENKEN game, shown running on Chrome, macOS, mobile Safari, iPhone and Android." /> + + +### Press highlights + +* “Flutter has grown by leaps and bounds to quickly become arguably the best framework for multi-platform coding across iOS and Android. […] It has frankly been a bit shocking how positive the response to Flutter has been.” ([XDA Developers](https://www.xda-developers.com/flutter-1-5-support-web-embedded-desktop/)) + +* “Google’s Flutter UI toolkit for cross-platform development may only be two years old, but it has quickly become the framework of choice for many developers.” ([TechCrunch](https://techcrunch.com/2019/05/07/googles-flutter-framework-spreads-its-wings-and-goes-multi-platform/)) + +* “Google is fixing one of the biggest hurdles for app developers that not only makes their jobs easier, but also makes their apps and services better to use on pretty much every platform we most commonly use, including Android, iOS, Windows 10, macOS, Chrome OS, and web browsers.” ([BusinessInsider](https://www.businessinsider.com/googles-flutter-will-bring-your-favorite-apps-to-mobile-desktop-web-2019-5)) + +* “Clearly, Google is trying to make Flutter the best way to create apps of every kind. Whether your app is for Android, iOS, Chrome OS, Windows, Web, IoT or all of the above doesn’t matter anymore, and that’s astounding.” ([9to5Google](https://9to5google.com/2019/05/07/flutter-apps-web-desktop-more/)) + +* “[The updates](https://developers.googleblog.com/2019/05/Flutter-io19.html), which got a lot of cheers when they were announced today […] mean that Flutter in fact becomes a multiplatform UI framework.” ([SiliconAngle](https://siliconangle.com/2019/05/07/google-expands-flutter-ui-framework-web/)) + +Also see articles from [VentureBeat](https://venturebeat.com/2019/05/07/google-expands-flutter-mobile-app-sdk-to-the-web-desktop-and-embedded-devices/), [ZDNet](https://www.zdnet.com/article/google-expands-ui-framework-flutter-from-just-mobile-to-multi-platform/), [Thurrott](https://www.thurrott.com/dev/206307/google-brings-flutter-to-the-web-in-technical-preview), and others. \ No newline at end of file diff --git a/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0506M98hmpxgRJFd7.webp b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0506M98hmpxgRJFd7.webp new file mode 100644 index 0000000000..fb27525af3 Binary files /dev/null and b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0506M98hmpxgRJFd7.webp differ diff --git a/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/05snhUrdCrAAauosx.webp b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/05snhUrdCrAAauosx.webp new file mode 100644 index 0000000000..dce7cdc4f9 Binary files /dev/null and b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/05snhUrdCrAAauosx.webp differ diff --git a/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0MeogiafDXLMMaxtX.gif b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0MeogiafDXLMMaxtX.gif new file mode 100644 index 0000000000..72730f48b7 Binary files /dev/null and b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0MeogiafDXLMMaxtX.gif differ diff --git a/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0UpUS0j53XBPfPB8j.webp b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0UpUS0j53XBPfPB8j.webp new file mode 100644 index 0000000000..4bf3f5b3a4 Binary files /dev/null and b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0UpUS0j53XBPfPB8j.webp differ diff --git a/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0X8hvqa_bbwsEtV-P.gif b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0X8hvqa_bbwsEtV-P.gif new file mode 100644 index 0000000000..dcf1875faf Binary files /dev/null and b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0X8hvqa_bbwsEtV-P.gif differ diff --git a/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0Y_VjMeKGuxCGCkSs.webp b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0Y_VjMeKGuxCGCkSs.webp new file mode 100644 index 0000000000..3ae9d5ac4e Binary files /dev/null and b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0Y_VjMeKGuxCGCkSs.webp differ diff --git a/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0clEvyPhHbCFI71Wc.webp b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0clEvyPhHbCFI71Wc.webp new file mode 100644 index 0000000000..d2acdf103c Binary files /dev/null and b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0clEvyPhHbCFI71Wc.webp differ diff --git a/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0dxy3KYQVwetiInqh.webp b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0dxy3KYQVwetiInqh.webp new file mode 100644 index 0000000000..6e65bd4bcb Binary files /dev/null and b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0dxy3KYQVwetiInqh.webp differ diff --git a/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0fouBgFDZseB3agw8.webp b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0fouBgFDZseB3agw8.webp new file mode 100644 index 0000000000..1d7b378eec Binary files /dev/null and b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0fouBgFDZseB3agw8.webp differ diff --git a/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0mhvYLQo_t9Sm_ecS.webp b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0mhvYLQo_t9Sm_ecS.webp new file mode 100644 index 0000000000..a1a37aa918 Binary files /dev/null and b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0mhvYLQo_t9Sm_ecS.webp differ diff --git a/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0pF3_jPjWNcie3LH0.webp b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0pF3_jPjWNcie3LH0.webp new file mode 100644 index 0000000000..56b6fd087a Binary files /dev/null and b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0pF3_jPjWNcie3LH0.webp differ diff --git a/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0wY_EYxQGJey5Vn4f.webp b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0wY_EYxQGJey5Vn4f.webp new file mode 100644 index 0000000000..931e8144de Binary files /dev/null and b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/0wY_EYxQGJey5Vn4f.webp differ diff --git a/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/1YhNtNltzEiKubvyWKUXKVw.webp b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/1YhNtNltzEiKubvyWKUXKVw.webp new file mode 100644 index 0000000000..e6c5d69605 Binary files /dev/null and b/sites/www/content/blog/accessibility-in-flutter-on-the-web/images/1YhNtNltzEiKubvyWKUXKVw.webp differ diff --git a/sites/www/content/blog/accessibility-in-flutter-on-the-web/index.md b/sites/www/content/blog/accessibility-in-flutter-on-the-web/index.md new file mode 100644 index 0000000000..016df389b8 --- /dev/null +++ b/sites/www/content/blog/accessibility-in-flutter-on-the-web/index.md @@ -0,0 +1,175 @@ +--- +title: "Accessibility in Flutter on the Web" +description: "How Flutter aims to make canvas-rendered apps accessible to users of assistive technologies" +publishDate: 2024-04-16 +author: tomayac +image: images/0pF3_jPjWNcie3LH0.webp +category: announcements +layout: blog +--- + +One of the target platforms the Flutter framework supports is the web. Flutter applications guarantee pixel perfection and platform consistency through rendering all UI onto a canvas element. However, by default canvas elements are not accessible. This case study explains how accessibility support works for such canvas-rendered Flutter apps. + +<DashImage figure src="images/1YhNtNltzEiKubvyWKUXKVw.webp" /> + + +Flutter has a large number of default widgets that [generate an accessibility tree](https://docs.flutter.dev/ui/accessibility-and-localization/accessibility?tab=browsers#:%7E:text=Flutter%E2%80%99s%20standard%20widgets%20generate%20an%20accessibility%20tree%20automatically.) automatically. An accessibility tree is a tree of accessibility objects that assistive technology can query for attributes and properties and perform actions on. For custom widgets, Flutter’s [`Semantics`](https://api.flutter.dev/flutter/widgets/Semantics-class.html) class lets developers describe the meaning of their widgets, helping assistive technologies make sense of the widget content. + +For performance reasons, at the time of this writing, Flutter’s accessibility is opt-in by default. The Flutter team would like to eventually turn the semantics on by default in Flutter Web. However, at the moment, this would lead to noticeable performance costs in a significant number of cases, and requires some optimization before the default can be changed. Developers who want to always turn on Flutter’s accessibility mode can do so with the following code snippet. + +```dart +import 'package:flutter/semantics.dart'; + +void main() { + runApp(const MyApp()); + if (kIsWeb) { + SemanticsBinding.instance.ensureSemantics(); + } +} +``` + +> Note: If your app absolutely requires to know if a user is using accessibility devices like screen readers, allow users to opt-in. + +Once you’ve opted in to Flutter’s accessibility support, the HTML changes automatically, as shown in the rest of this page. +> **Note:** Screen readers are only one example of assistive technology that profits from the described approach. For improved legibility, screen readers are used as a proxy for this and other assistive technologies in general. + +## Flutter’s accessibility opt-in + +Flutter’s opt-in mechanism is a hidden button. It places a button, exactly speaking, an `<flt-semantics-placeholder>` element with `role="button"` — which is invisible and unreachable to sighted users — in its HTML. It’s a custom element with styling applied so it doesn’t show and isn’t selectable unless you use a screen reader. + +```xml +<flt-semantics-placeholder + role="button" + aria-live="polite" + aria-label="Enable accessibility" + tabindex="0" + style=" + position: absolute; + left: -1px; + top: -1px; + width: 1px; + height: 1px;" +></flt-semantics-placeholder> + +/* `<flt-semantics-placeholder>` inherits from `<flutter-view>`. */ +flutter-view { + user-select: none; +} +``` + + +## Changes after the opt-in + +What happens when a screen reader user clicks this button? Consider a not too complex example like the [card](https://flutter-gallery-archive.web.app/#/demo/card) from the Flutter Gallery as displayed in the following screenshot. + +<DashImage figure src="images/0mhvYLQo_t9Sm_ecS.webp" /> + + +To better understand what’s changing when a user clicks the button, compare the before and after screenshots of Chrome DevTools when you [inspect the accessibility tree](https://developer.chrome.com/blog/full-accessibility-tree/). The second screenshot exposes a lot more semantic information than the first. + +**Before opt-in:** + +<DashImage figure src="images/0Y_VjMeKGuxCGCkSs.webp" /> + + +**After opt-in:** + +<DashImage figure src="images/0dxy3KYQVwetiInqh.webp" /> + + +## Details of the implementation + +The core idea in Flutter is to create an accessible DOM structure that reflects what’s right now displayed on the canvas. This consists of an `<flt-semantics-host>` parent custom element that has `<flt-semantics>` and `<flt-semantics-container>` child elements that in turn can be nested. Consider a button widget, such as [`TextButton`](https://api.flutter.dev/flutter/material/TextButton-class.html). This widget is represented by an `<flt-semantics>` element in the DOM. The ARIA annotations (e.g., [`role`](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/role) or [`aria-label`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label)) and other DOM properties ([`tabindex`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex), event listeners) on the `<flt-semantics>` element allows the screen reader to announce the element as a button to the user, and support clicking and tapping on it, even though it’s not a literal [`<button>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) element. In the following screenshot the **Share** button is one example of such a button. + +<DashImage figure src="images/05snhUrdCrAAauosx.webp" /> + + +This `<flt-semantics>` element is absolutely positioned to appear exactly at the position where the corresponding button is painted on the canvas. This is because Flutter owns the layout of all widgets and it precomputes the positions and sizes of every semantic node. Absolute layout allows placing the accessibility element exactly where the user would expect it. However, what this also means is that whenever the user scrolls, the positions need to be adjusted, which can be expensive in some situations. + +<DashImage figure src="images/0pF3_jPjWNcie3LH0.webp" /> + + +## Expanding the approach to all default widgets + +Since Flutter knows that what is represented as `<flt-semantics role="button">` in the DOM structure in the Flutter source code originally was a Flutter [`TextButton`](https://api.flutter.dev/flutter/material/TextButton-class.html), it’s relatively easy to expand the approach and create a mapping from all the existing [Flutter widgets](https://docs.flutter.dev/reference/widgets) to the corresponding [WAI-ARIA roles](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles), which is exactly what Flutter does out of the box for all its default widgets. For example, Flutter supports the following roles today: + +* Text + +* Buttons + +* Checkboxes + +* Radio boxes + +* Text fields + +* Links + +* Dialogs + +* Images + +* Sliders + +* Live regions + +* Scrollables + +* Containers and groups + +Note that even though the list of roles is short, many different categories of widgets frequently share the same role. For example, Material [`TextField`](https://api.flutter.dev/flutter/material/TextField-class.html) and [`CupertinoTextField`](https://api.flutter.dev/flutter/cupertino/CupertinoTextField-class.html) can share the same text field role. Most layout widgets, such as Stack, Column, Row, Flex, etc., can all be represented by a container/group. + +## Challenges with custom widgets + +When building a custom widget, Flutter may not be able to automatically apply a correct role to it. If a widget is simply a decorated variant of an existing widget (e.g., a wrapper over [`EditableText`](https://api.flutter.dev/flutter/widgets/EditableText-class.html)), it may present itself correctly (as a text field). However, if you are building a widget from scratch, Flutter expects you to use the [`Semantics`](https://api.flutter.dev/flutter/widgets/Semantics-class.html) widget to describe its accessibility properties. WAI-ARIA defines many different widget roles. Flutter only supports a subset of roles, although this subset is continuously growing. + +For example, you can explore the team class picker live in the [I/O Flip game](https://flip.withgoogle.com/), as shown in the following screenshot. In web terms, it’s essentially a `<select>`, or a [`listbox`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/listbox_role)in WAI-ARIA terms. And while the available options are represented as [`generic`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/generic_role)texts (they should rather be [`<option>`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/option_role) elements), an even bigger problem is that it’s not clear from the accessibility tree that there are more options to choose from but that are outside of the viewport of the widget. Note the available options in the accessibility tree before and after scrolling. + +**Before scrolling:** + +<DashImage figure src="images/0fouBgFDZseB3agw8.webp" /> + + +**After scrolling:** + +<DashImage figure src="images/0wY_EYxQGJey5Vn4f.webp" /> + + +If you look at the [source code](https://github.com/flutter/io_flip/blob/a128c2ed1afcfbbd79edf2dfc21682bcc0dc3067/lib/prompt/view/prompt_form_view.dart), you can see that it doesn’t use the [`Semantics`](https://api.flutter.dev/flutter/widgets/Semantics-class.html) class, since Semantics doesn’t support the listbox and option role annotation use case yet. But it does use a [`ListWheelScrollView`](https://api.flutter.dev/flutter/widgets/ListWheelScrollView-class.html), which is similar to a regular [`ListView`](https://api.flutter.dev/flutter/widgets/ListView-class.html), so it knows it’s dealing with a list. Note, though, how the accessibility tree only ever shows the now visible items, plus a few items above and below the viewport, but never all items. (This is a common app performance trick that we almost got natively on the web, too, in the form of a [`<virtual-scroller>`](https://github.com/WICG/virtual-scroller).) + +<DashImage figure src="images/0506M98hmpxgRJFd7.webp" /> + + +Compare Flutter’s accessibility tree to that of the [scrollable listbox example](https://www.w3.org/WAI/ARIA/apg/patterns/listbox/examples/listbox-scrollable/) from the [ARIA Authoring Practices Guide](https://www.w3.org/WAI/ARIA/apg/patterns/), where all options are shown in the accessibility tree, even those outside of the viewport. Not fully supporting this listbox use case is at the time of this writing a shortcoming of the Flutter solution that will get addressed in the future. + +<DashImage figure src="images/0clEvyPhHbCFI71Wc.webp" /> + + +## Text editing + +Flutter has an `<flt-text-editing-host>` element that has either an `<input>`or a `<textarea>` as its child that it places pixel-perfectly onto the corresponding canvas area. This means browser conveniences like autofill work as expected. This feature is always enabled, independent of whether accessibility is enabled or not. In the semantics tree, the text field is represented by an `<input>` element, potentially with an ARIA label describing it. The following [text field](https://flutter-gallery-archive.web.app/#/demo/text-field) example is from the Flutter Gallery. See how the `<input>` field is dynamically repositioned whenever the user presses the tab key. + +<DashImage figure src="images/0MeogiafDXLMMaxtX.gif" /> + + +While for sighted users the label texts displayed in the text inputs are visible, for screen reader users the text fields are announced as “edit, blank” with [NVDA](https://www.nvaccess.org/about-nvda/) on Windows or “edit text, blank” with [VoiceOver](https://www.apple.com/voiceover/info/guide/_1121.html) on macOS, since Flutter at the moment doesn’t create `<label>` elements yet. You can see VoiceOver’s screen reader output at the bottom of the images. This is something Flutter will fix in the future. + +<DashImage figure src="images/0UpUS0j53XBPfPB8j.webp" /> + + +When text fields are properly labeled, the screen reader announces the intended meaning, as depicted in the following pure HTML example. + +<DashImage figure src="images/0X8hvqa_bbwsEtV-P.gif" /> + + +## Conclusions + +This case study has delved into the intricacies of how accessibility support functions within Flutter canvas applications on the web. Flutter’s accessibility unfolds through a hidden button with specific attributes and styling. Upon activation, this approach significantly improves the experience for users relying on screen readers and other assistive technologies. The core concept in Flutter involves creating an accessible DOM structure that mirrors the canvas display, utilizing custom elements such as `<flt-semantics-host>`, `<flt-semantics>`, `<flt-semantics-container>`, and others. + +While Flutter adeptly maps default widgets to WAI-ARIA roles, the team acknowledges some remaining challenges. The exploration of text editing in Flutter showcases the trick with the `<flt-text-editing-host>` with `<input>` or `<textarea>`, demonstrating dynamic repositioning of input fields. + +Looking ahead, there are opportunities for further refinement of Flutter’s accessibility framework that the team has already started working on. This includes addressing the listbox use case for custom widgets and enhancing label element creation for text editing. These anticipated enhancements aim to deliver a more encompassing and seamless accessibility experience, reflecting Flutter’s commitment to continuous improvement of its web compilation target. + +## Acknowledgements + +This case study was reviewed for accuracy by [Yegor Jbanov](https://www.linkedin.com/in/yegorjbanov/), [Kevin Moore](https://www.linkedin.com/in/kevmoo/), [Michael Thomsen](https://www.linkedin.com/in/msthomsen/), and [Shams Zakhour](https://www.linkedin.com/in/shams-zakhour-84a9b31) from the Flutter team. The editorial review is courtesy of [Rachel Andrew](https://rachelandrew.co.uk/) and [Shams Zakhour](https://www.linkedin.com/in/shams-zakhour-84a9b31). \ No newline at end of file diff --git a/sites/www/content/blog/accessible-expression-with-material-icons-and-flutter/images/1kcT41A5GvXp8XrjHjiuc4A.webp b/sites/www/content/blog/accessible-expression-with-material-icons-and-flutter/images/1kcT41A5GvXp8XrjHjiuc4A.webp new file mode 100644 index 0000000000..4a614d574b Binary files /dev/null and b/sites/www/content/blog/accessible-expression-with-material-icons-and-flutter/images/1kcT41A5GvXp8XrjHjiuc4A.webp differ diff --git a/sites/www/content/blog/accessible-expression-with-material-icons-and-flutter/images/1sZKrpmVM5f5bRSgy453T1Q.webp b/sites/www/content/blog/accessible-expression-with-material-icons-and-flutter/images/1sZKrpmVM5f5bRSgy453T1Q.webp new file mode 100644 index 0000000000..ffbdfdb0bd Binary files /dev/null and b/sites/www/content/blog/accessible-expression-with-material-icons-and-flutter/images/1sZKrpmVM5f5bRSgy453T1Q.webp differ diff --git a/sites/www/content/blog/accessible-expression-with-material-icons-and-flutter/index.md b/sites/www/content/blog/accessible-expression-with-material-icons-and-flutter/index.md new file mode 100644 index 0000000000..3c4743305f --- /dev/null +++ b/sites/www/content/blog/accessible-expression-with-material-icons-and-flutter/index.md @@ -0,0 +1,53 @@ +--- +title: "Accessible expression with Material Icons and Flutter" +description: "Material Design and Flutter both help developers build experiences that are flexible, accessible, and expressive…" +publishDate: 2020-11-04 +author: andremobilelabonte +image: images/1kcT41A5GvXp8XrjHjiuc4A.webp +category: announcements +layout: blog +--- + +Material Design and Flutter both help developers build experiences that are flexible, accessible, and expressive, while delivering great performance and efficiency. Now we’re pleased to give you even more ways to create distinctive UIs with improved support for Material Icons in Flutter. + +[Material Icons](https://material.io/resources/icons) are beautifully crafted symbols for common actions and items, including everything from simple arrows and indicators for navigation, to symbols representing concepts like accessibility, bug reporting, and even hand washing. + +Flutter now has support for all four icon styles — *filled, outlined, sharp, and round* — to help you make your app or web site familiar and accessible for your users, while allowing you to uniquely communicate your brand. And with support for tree-shaking, Flutter optimizes your app build to include only the symbols you’re using, making load times and memory use more efficient. + +Here are a few icons that showcase the range of options you’ll find in Material’s catalog. You can browse the full set of icons and download bitmap or vector versions for use in your favorite design tool on [Material.io](https://material.io/resources/icons/), or view Flutter’s versions on the [`Icons`](https://api.flutter.dev/flutter/material/Icons-class.html) API page. + +<DashImage figure src="images/1kcT41A5GvXp8XrjHjiuc4A.webp" alt="Sample of 4 icons: shopping_cart, chat, masks, wash. Displayed in the 4 supported styles: filled, outlined, rounded, sharp." caption="Sample of 4 icons: shopping_cart, chat, masks, wash. Displayed in the 4 supported styles: filled, outlined, rounded, sharp." /> + + +**Getting started** + +To get started with the [`Icon` class](https://master-api.flutter.dev/flutter/widgets/Icon-class.html), make sure to set `uses-material-design: true` in your project’s `pubspec.yaml` file. This tells Flutter to include the icon assets in your application. + +Icons in Flutter are delivered as vectors via an icon font, so you can infinitely adjust size and color without worrying about losing image quality. Checkout [GitHub](https://github.com/material-components/material-components-flutter/tree/develop/medium/material_icons) for the code that generated the set of icons above. + +**Don’t skip the semantic label!** + +In order for users of assistive technologies like screen readers to effectively navigate your app or site, it’s important to provide a semantic label that is meaningful and contextually appropriate. + +By default screen readers read aloud any text that’s visible onscreen. To accurately translate visual elements like icons into text-based UI, you need to thoughtfully label those elements. + +For example, when an icon is used in conjunction with an action element, like a button, you should always set your semantic label to a string that describes what happens when a user clicks on it. + +[Material.io offers guidance](https://material.io/design/usability/accessibility.html#writing) on indicating UI elements by action. A common mistake is to default the semantic label to the name of the icon, as opposed to the action that is performed when that icon is selected. + +<DashImage figure src="images/1sZKrpmVM5f5bRSgy453T1Q.webp" /> + + +To learn more about the importance of building accessible digital experiences, see [Accessibility](https://flutter.dev/docs/development/accessibility-and-localization/accessibility) on flutter.dev. + +**Only ship what you use!** + +When you build your app for release, the Flutter compiler performs “tree shaking”, removing unused code and assets (including icons) to optimize your app’s footprint and help minimize download and load times. Use all the icons you want! During compilation, a custom icon font is generated, containing only the subset of icons used in your project. This offers great reduction in binary size — everybody wins! + +In addition to tree-shaking, here is a great community post about [best practices for reducing Flutter app size](https://medium.com/@suryadevsingh24032000/size-matters-reducing-flutter-app-size-best-practices-ca992207782). + +**Show us what you’ve got!** + +Developers all around the world are using Material icons to ensure readability and clarity at both large and small sizes; these icons have been optimized for beautiful display on all of Flutter’s supported platforms and display resolutions. + +Follow us at @[MaterialDesign](https://twitter.com/materialdesign) [@FlutterDev](https://twitter.com/FlutterDev) and show how you are using Material Design and Flutter to create beautiful, accessible digital experiences! \ No newline at end of file diff --git a/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/images/1G0_f2A0k1sK7aWmFbKAFlg.webp b/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/images/1G0_f2A0k1sK7aWmFbKAFlg.webp new file mode 100644 index 0000000000..b2f6df1b8c Binary files /dev/null and b/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/images/1G0_f2A0k1sK7aWmFbKAFlg.webp differ diff --git a/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/images/1IURgMIU9x8zzm-p6lZUNwg.webp b/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/images/1IURgMIU9x8zzm-p6lZUNwg.webp new file mode 100644 index 0000000000..d1c60d0e75 Binary files /dev/null and b/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/images/1IURgMIU9x8zzm-p6lZUNwg.webp differ diff --git a/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/images/1PpfHUeUH0sr5vDUUdcG81A.webp b/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/images/1PpfHUeUH0sr5vDUUdcG81A.webp new file mode 100644 index 0000000000..e3c82035bc Binary files /dev/null and b/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/images/1PpfHUeUH0sr5vDUUdcG81A.webp differ diff --git a/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/images/1RA_cBWkp9GOQsK_H0-Dg8w.gif b/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/images/1RA_cBWkp9GOQsK_H0-Dg8w.gif new file mode 100644 index 0000000000..f41d420203 Binary files /dev/null and b/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/images/1RA_cBWkp9GOQsK_H0-Dg8w.gif differ diff --git a/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/images/1Y_ROvg5zl7edWeA-G6THzg.webp b/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/images/1Y_ROvg5zl7edWeA-G6THzg.webp new file mode 100644 index 0000000000..83658cb21a Binary files /dev/null and b/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/images/1Y_ROvg5zl7edWeA-G6THzg.webp differ diff --git a/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/images/1g3j3u2aTh1m5pR4M5CqX_w.webp b/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/images/1g3j3u2aTh1m5pR4M5CqX_w.webp new file mode 100644 index 0000000000..fc7cedc3af Binary files /dev/null and b/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/images/1g3j3u2aTh1m5pR4M5CqX_w.webp differ diff --git a/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/index.md b/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/index.md new file mode 100644 index 0000000000..93eda68e33 --- /dev/null +++ b/sites/www/content/blog/adapting-wonderous-to-larger-device-formats/index.md @@ -0,0 +1,67 @@ +--- +title: "Adapting Wonderous to larger device formats" +description: "Using Flutter to design mobile applications that look beautiful on all devices" +publishDate: 2023-01-25 +author: leighajarett +image: images/1RA_cBWkp9GOQsK_H0-Dg8w.gif +category: spotlight +layout: blog +--- + +Today, we’re announcing the next version of Wonderous, which adds support for foldable devices, tablet, and tablet landscape. This iteration allowed us to test Flutter’s ability to **adapt to different device formats**, and the [code](https://github.com/gskinnerTeam/flutter-wonderous-app) repo on GitHub provides a reference for leveraging [adaptive design](https://docs.flutter.dev/development/ui/layout/building-adaptive-apps). + +In this post, we’ll walk through some of what we learned. While our focus was on apps for handheld devices, many of these concepts can be adapted to desktop and web. + +## Narrowing focus + +When thinking through adaptive design, designers should first ask *“What form factors are we possibly supporting?”* Flutter supports nearly every device, but app creators should be explicit about what formats their app has been designed and tested for. Narrowing the focus allows designers to carefully consider how to optimize the digital experience for the canvas that is available. Without careful design, layouts suited for mobile can look unpolished on larger form factors. + +## Taking advantage of extra real estate + +When adapting the design of an app to larger screen sizes, designers should consider how to take advantage of the extra real estate. This might mean enhancing the presentation by showing a different layout. For example: + +<DashImage figure src="images/1Y_ROvg5zl7edWeA-G6THzg.webp" alt="*iPad Pro 12.9-inch portrait and landscape*" caption="*iPad Pro 12.9-inch portrait and landscape*" /> + + +Adding additional rows or columns: + +<DashImage figure src="images/1G0_f2A0k1sK7aWmFbKAFlg.webp" alt="*iPad Pro 12.9-inch and iPhone 11*" caption="*iPad Pro 12.9-inch and iPhone 11*" /> + + +Or changing columns to rows: + +<DashImage figure src="images/1g3j3u2aTh1m5pR4M5CqX_w.webp" alt="*iPad Pro 12.9-inch portrait and landscape*" caption="*iPad Pro 12.9-inch portrait and landscape*" /> + + +Alternatively, you might use the extra canvas to do more of the same. Wonderous features beautiful illustrations so the larger screen sizes allowed us to showcase even more of this work: + +<DashImage figure src="images/1PpfHUeUH0sr5vDUUdcG81A.webp" alt="*iPhone 11, Samsung Fold4 closed, iPad Pro 12.9-inch*" caption="*iPhone 11, Samsung Fold4 closed, iPad Pro 12.9-inch*" /> + + +**Consider approaches for how UI components can react** + +As the screen becomes larger, you’ll need to consider how your app’s UI components will react. For example, you might scale the component to fit the available space: + +<DashImage figure src="images/1IURgMIU9x8zzm-p6lZUNwg.webp" alt="*Samsung Fold4 open and iPhone 11*" caption="*Samsung Fold4 open and iPhone 11*" /> + + +You might also consider keeping the content center weighted, or pinning certain assets to the device bounds: + +<DashImage figure src="images/1RA_cBWkp9GOQsK_H0-Dg8w.gif" /> + + +**Adaptive design with Flutter** + +There are many features in the Flutter framework that were useful for adapting Wonderous. For example: + +* Hot reload empowered the design team to make small adjustments in real time + +* Dynamic padding and font sizes + +* The [Flex](https://api.flutter.dev/flutter/widgets/Flex-class.html) widget dynamically displays either a row or a column + +* [MediaQuery](https://api.flutter.dev/flutter/widgets/MediaQuery-class.html) returns details like screen size and device orientation + +You can read more specifics on the technical implementations on [gSkinner’s website](https://blog.gskinner.com/archives/2023/01/flutter-adapting-wonderous-for-landscape.html). You can also learn more about designing apps for larger screen sizes on Android in [Large screen app sizes](https://developer.android.com/docs/quality-guidelines/large-screen-app-quality) on the Android docs site. + +We hope you’ll install Wonderous and leverage the code as a reference while developing your own apps. If you have any feedback on the app itself, please [file an issue on Github](https://github.com/gskinnerTeam/flutter-wonderous-app). If you have any thoughts on adaptive design with Flutter, feel free to send me a message on [Twitter](https://twitter.com/LeighaJarett) (@leighajarett) or [LinkedIn](https://www.linkedin.com/in/leighajarett/) (Leigha Jarett). \ No newline at end of file diff --git a/sites/www/content/blog/all-the-flutter-news-from-a-busy-google-i-o-2024/images/1--DvqdXSA38rPuqMK5c0tQ.webp b/sites/www/content/blog/all-the-flutter-news-from-a-busy-google-i-o-2024/images/1--DvqdXSA38rPuqMK5c0tQ.webp new file mode 100644 index 0000000000..772a0b74cb Binary files /dev/null and b/sites/www/content/blog/all-the-flutter-news-from-a-busy-google-i-o-2024/images/1--DvqdXSA38rPuqMK5c0tQ.webp differ diff --git a/sites/www/content/blog/all-the-flutter-news-from-a-busy-google-i-o-2024/index.md b/sites/www/content/blog/all-the-flutter-news-from-a-busy-google-i-o-2024/index.md new file mode 100644 index 0000000000..7f3290b674 --- /dev/null +++ b/sites/www/content/blog/all-the-flutter-news-from-a-busy-google-i-o-2024/index.md @@ -0,0 +1,67 @@ +--- +title: "All the Flutter news from a busy Google I/O 2024" +description: "Catch up on all the big product updates, new resources, and success stories announced at Google I/O 2024" +publishDate: 2024-05-30 +author: mit-mit +image: images/1--DvqdXSA38rPuqMK5c0tQ.webp +category: events +layout: blog +--- + +It’s hard to believe that [Google I/O 2024](https://io.google/2024/) was already two weeks ago. We couldn’t be more grateful for the Flutter community’s support — the energy was uplifting! + +<DashImage figure src="images/1--DvqdXSA38rPuqMK5c0tQ.webp" alt="Flutter logo illustration" caption="Flutter logo illustration" /> + + +We shared a lot of product updates, resources, developer stories, technical sessions, and workshops, across several blogs, videos, and events — and we know it can be tough to keep track of it all. + +Below, you’ll find an overview of everything we shared at Google I/O 2024, as well as updates on locations and dates for events where you might be able to connect with us and other Flutter community members at I/O Connect in Berlin, Bengaluru, Beijing, and I/O Extended events all summer long. + +<YoutubeEmbed id="x2WOHonEwqM" title="" fullwidth="true"/> + + +## Blog posts + +We launched **four new blog posts** across the Dart and Medium blogs: + +* [Landing Flutter 3.22 and Dart 3.4 at Google I/O 2024](https://medium.com/flutter/io24-5e211f708a37) + +* [What’s new in Flutter 3.22](https://medium.com/flutter/fbde6c164fe3) + +* [Announcing the winners of the Global Gamers Challenge](https://medium.com/flutter/announcing-the-winners-of-the-global-gamers-challenge-1ccf4d271226) + +* [Announcing Dart 3.4](https://medium.com/dartlang/dart-3-4-bd8d23b4462a) + +We also partnered with the Android team on a blog post to offer some guidance on **how to choose the right framework** for your project; Flutter, Jetpack Compose, and Kotlin Multi-Platform: + +* [Making development across platforms easier for developers](https://developers.googleblog.com/en/making-development-across-platforms-easier-for-developers/) + +## Google I/O talks, codelabs, and workshops + +The main Google I/O event featured a selection of exciting talks on Dart and Flutter. We recommend starting with the **[What’s new in Flutter](https://www.youtube.com/watch?v=lpnKWK-KEYs) keynote**, and then moving on to our [five technical sessions](https://io.google/2024/explore/?q=technical-session,flutter), which feature topics such as CameraX, adaptive UI, AI agents, Gemini, and app deep links. + +We also have [three new codelabs](https://io.google/2024/explore/?q=codelab,flutter), all focused on creating [casual games with Flutter](https://flutter.dev/games). They can help you get started with 2D physics and adding sound & music. For even more detail, check out the new [2D physics games workshop](https://io.google/2024/explore/?q=workshop,flutter). + +## New Flutter showcase stories + +The [Flutter Showcase](https://flutter.dev/showcase) exhibits a selection of Flutter apps that illustrate compelling user experiences you can create with Flutter across mobile, web, and desktop. We added **four new showcase stories** for Google I/O 2024: + +* [Universal Studios](https://flutter.dev/showcase/universal-studios): *Universal Studios build next generation experiences with Flutter* + +* [Supercell](https://flutter.dev/showcase/supercell): *Flutter helps Supercell reduce Supercell ID core code size by 45%* + +* [Etermax](https://flutter.dev/showcase/etermax): *Etermax boosts developer efficiency with Flutter* + +* [Virgin Money](https://flutter.dev/showcase/virgin-money): *Virgin Money unifies app development with Flutter* + +## I/O Connect events + +Google I/O did not end with the [main event](https://io.google/2024/) at Shoreline in Mountain View, California, USA. There are a number of [Google I/O community events](https://gdg.community.dev/ioextended/) across the globe, plus [Google I/O Connect](https://developers.google.com/events) events planned in three locations: + +* I/O Connect Berlin, Germany on 27 June 2024 + +* I/O Connect Bengaluru, India on 17 July 2024 + +* I/O Connect Beijing, China on 7–8 August 2024 + +The Dart and Flutter teams will be represented at all three I/O Connect events, and we hope to connect with many of you! If you’d like to join an event, make sure to [register](https://developers.google.com/events). We look forward to connecting! \ No newline at end of file diff --git a/sites/www/content/blog/android-dark-theme-in-flutter/images/1b5P4ZxNmuw2q6-cZwj1Ukw.webp b/sites/www/content/blog/android-dark-theme-in-flutter/images/1b5P4ZxNmuw2q6-cZwj1Ukw.webp new file mode 100644 index 0000000000..a5a93247c8 Binary files /dev/null and b/sites/www/content/blog/android-dark-theme-in-flutter/images/1b5P4ZxNmuw2q6-cZwj1Ukw.webp differ diff --git a/sites/www/content/blog/android-dark-theme-in-flutter/images/1dGPFsh1dbSaoNOGQCpsmCw.webp b/sites/www/content/blog/android-dark-theme-in-flutter/images/1dGPFsh1dbSaoNOGQCpsmCw.webp new file mode 100644 index 0000000000..2eb08b758f Binary files /dev/null and b/sites/www/content/blog/android-dark-theme-in-flutter/images/1dGPFsh1dbSaoNOGQCpsmCw.webp differ diff --git a/sites/www/content/blog/android-dark-theme-in-flutter/images/1jNi8NVhtMe7zo_3Nmtajqg.webp b/sites/www/content/blog/android-dark-theme-in-flutter/images/1jNi8NVhtMe7zo_3Nmtajqg.webp new file mode 100644 index 0000000000..c06c43362e Binary files /dev/null and b/sites/www/content/blog/android-dark-theme-in-flutter/images/1jNi8NVhtMe7zo_3Nmtajqg.webp differ diff --git a/sites/www/content/blog/android-dark-theme-in-flutter/images/1wogDV86zrS8BNYI-8Th-TA.jpeg b/sites/www/content/blog/android-dark-theme-in-flutter/images/1wogDV86zrS8BNYI-8Th-TA.jpeg new file mode 100644 index 0000000000..1f1a6ec463 Binary files /dev/null and b/sites/www/content/blog/android-dark-theme-in-flutter/images/1wogDV86zrS8BNYI-8Th-TA.jpeg differ diff --git a/sites/www/content/blog/android-dark-theme-in-flutter/index.md b/sites/www/content/blog/android-dark-theme-in-flutter/index.md new file mode 100644 index 0000000000..736967742b --- /dev/null +++ b/sites/www/content/blog/android-dark-theme-in-flutter/index.md @@ -0,0 +1,56 @@ +--- +title: "Android Dark Theme in Flutter" +description: "A week ago at Google I/O 2019, the Android team announced support in Android Q for Dark Theme, a system-wide dark UI mode that is toggled…" +publishDate: 2019-05-15 +author: matthew-carroll +image: images/1wogDV86zrS8BNYI-8Th-TA.jpeg +category: announcements +layout: blog +--- + +<DashImage figure src="images/1wogDV86zrS8BNYI-8Th-TA.jpeg" /> + + +## Android Dark Theme in Flutter + +A week ago at Google I/O 2019, the Android team [announced support in Android Q for Dark Theme](https://developer.android.com/preview/features/darktheme), a system-wide dark UI mode that is toggled by the user. This then begs the question, when will Flutter add support for this awesome new Android feature? Well, you might be surprised to find out that [Flutter added support for dark theme back in February](https://github.com/flutter/flutter/pull/26605)! Here’s how to use it. + +First, let’s start with a typical *MaterialApp* that establishes the theme for the rest of the UI. The following is how a typical app would define its theme before Dark Theme. + +```dart +MaterialApp( + theme: ThemeData( + brightness: Brightness.light, + primaryColor: Colors.red, + ), +); +``` + +<DashImage figure src="images/1jNi8NVhtMe7zo_3Nmtajqg.webp" /> + + +In Flutter, we introduced a *darkTheme* property for *MaterialApp*, which allows you to specify a 2nd, independent theme to be used just for Android’s Dark Theme feature. Let’s introduce a theme with a setting of *Brightness.dark* for our *MaterialApp*’s *darkTheme*. + +```dart +MaterialApp( + theme: ThemeData( + brightness: Brightness.light, + primaryColor: Colors.red, + ), + darkTheme: ThemeData( + brightness: Brightness.dark, + ), +); +``` + +<DashImage figure src="images/1b5P4ZxNmuw2q6-cZwj1Ukw.webp" /> + + +Tada! Now when you toggle Dark Theme in your system drawer, your Flutter app will automatically switch from your regular *theme* to your *darkTheme*! + +<DashImage figure src="images/1dGPFsh1dbSaoNOGQCpsmCw.webp" /> + + +Thanks to Flutter’s use of code for UI, updating your app to support a great new feature takes little more than adding a new property to your widget tree. No XML styles to update, no CSS to update, just a normal Dart parameter. + +In Flutter, there’s no reason to be afraid of the dark. \ No newline at end of file diff --git a/sites/www/content/blog/animation-deep-dive/images/1-T1fn-7fK2OU-z711XG_8A.gif b/sites/www/content/blog/animation-deep-dive/images/1-T1fn-7fK2OU-z711XG_8A.gif new file mode 100644 index 0000000000..c74c4195ad Binary files /dev/null and b/sites/www/content/blog/animation-deep-dive/images/1-T1fn-7fK2OU-z711XG_8A.gif differ diff --git a/sites/www/content/blog/animation-deep-dive/images/14bIqmJ8DQLSnlFtkN6bjaA.webp b/sites/www/content/blog/animation-deep-dive/images/14bIqmJ8DQLSnlFtkN6bjaA.webp new file mode 100644 index 0000000000..491fd35e87 Binary files /dev/null and b/sites/www/content/blog/animation-deep-dive/images/14bIqmJ8DQLSnlFtkN6bjaA.webp differ diff --git a/sites/www/content/blog/animation-deep-dive/images/1nKjFR7DVd-2r7_sSgBprfA.webp b/sites/www/content/blog/animation-deep-dive/images/1nKjFR7DVd-2r7_sSgBprfA.webp new file mode 100644 index 0000000000..ff8c96d8c6 Binary files /dev/null and b/sites/www/content/blog/animation-deep-dive/images/1nKjFR7DVd-2r7_sSgBprfA.webp differ diff --git a/sites/www/content/blog/animation-deep-dive/images/1uuyMl4qHr_bFZJENdMDimQ.webp b/sites/www/content/blog/animation-deep-dive/images/1uuyMl4qHr_bFZJENdMDimQ.webp new file mode 100644 index 0000000000..0833e0dbc7 Binary files /dev/null and b/sites/www/content/blog/animation-deep-dive/images/1uuyMl4qHr_bFZJENdMDimQ.webp differ diff --git a/sites/www/content/blog/animation-deep-dive/images/1y4Pbt8ehWTAEfL9_M8Aoog.webp b/sites/www/content/blog/animation-deep-dive/images/1y4Pbt8ehWTAEfL9_M8Aoog.webp new file mode 100644 index 0000000000..f6cc6545d8 Binary files /dev/null and b/sites/www/content/blog/animation-deep-dive/images/1y4Pbt8ehWTAEfL9_M8Aoog.webp differ diff --git a/sites/www/content/blog/animation-deep-dive/index.md b/sites/www/content/blog/animation-deep-dive/index.md new file mode 100644 index 0000000000..5e8c23d4f7 --- /dev/null +++ b/sites/www/content/blog/animation-deep-dive/index.md @@ -0,0 +1,295 @@ +--- +title: "Animation deep dive" +description: "Last year, I got to record one of the episodes in the Flutter Animations series, and I thought I’d publish the same content for those who…" +publishDate: 2020-04-17 +author: filiph +image: images/14bIqmJ8DQLSnlFtkN6bjaA.webp +layout: blog +--- + +Last year, I got to record one of the episodes in the Flutter Animations series, and I thought I’d publish the same content for those who prefer text over video. + +<YoutubeEmbed id="PbcILiN8rbo" title="Animation deep dive - Flutter in Focus" fullwidth="true"/> + + +In the other episodes of the series, my colleagues talk about all the practical ways to build animations in Flutter. Not so in my episode. Here, you’ll learn how to implement animations in the *least* pragmatic way imaginable. (But, you’ll also learn some things along the way.) + +Let’s start with something simple and lighthearted: + +### What *is* motion, really? + +You see, motion is an illusion. Look at this: + +<DashImage figure src="images/14bIqmJ8DQLSnlFtkN6bjaA.webp" alt="A video of Filip waving his hand." caption="A video of Filip waving his hand." /> + + +It’s a lie. What you’re actually seeing are many still images shown in quick succession. This is how movies work. The individual pictures are called frames in cinema— and because digital screens work similarly— they’re called frames here too. Cinema normally shows 24 frames per second. Modern digital devices show 60 to 120 frames per second. + +So, if motion is a lie, what are all these `AnimationFoo` and `FooTransition` widgets really doing? Surely, because the frames need to be constructed up to 120 times per second, the UI cannot be *rebuilt* every time. + +### Or, can it? + +In fact, animations in Flutter are just a way to rebuild parts of your widget tree on every frame. There is no special case. Flutter is fast enough to do that. + +Let’s look at one of the building blocks of Flutter animations: `AnimatedBuilder`. This widget is an `AnimatedWidget`, which is backed by `_AnimatedState`. In the State’s `initState()` method, we are listening on the `Animation` (or `Listenable`, as it is called here), and when it changes its value, we … call `setState()`. + +<DashImage figure src="images/1-T1fn-7fK2OU-z711XG_8A.gif" alt="This confusing screencast is just showing that I am telling the truth in the previous paragraph. Animated Builder really does call `setState()` on every frame." caption="This confusing screencast is just showing that I am telling the truth in the previous paragraph. Animated Builder really does call `setState()` on every frame." /> + + +There you go. Animations in Flutter are just a quick succession of changing the state of some widget, 60 to 120 times per second. + +I can prove it. Here’s an animation that “animates” from zero to the speed of light. Although it’s changing the text on every frame, from Flutter’s perspective, it’s just another animation. + +<DashImage figure src="images/1y4Pbt8ehWTAEfL9_M8Aoog.webp" /> + + +Let’s use Flutter’s animation framework to build that animation from first principles. + +Normally, we would use the `TweenAnimationBuilder` widget or something similar, but in this article, we’ll ignore all that, and go with a ticker, a controller, and `setState`. + +## Ticker + +Let’s talk about Ticker first. 99% of the time, you won’t use a ticker directly. But, I think it’s still helpful to talk about it — even if only to demystify it. + +A ticker is an object that calls a function for every frame. + +``` +**var** ticker = Ticker((elapsed) => print(**'hello'**)); +ticker.start(); +``` + + +In this case, we’re printing ‘hello’ every frame. Admittedly, that’s not very useful. + +Also, we forgot to call `ticker.dispose()`, so now our ticker will go on forever, until we kill the app. + +That’s why Flutter gives you `SingleTickerProviderStateMixin`, the aptly named mixin you’ve seen in some of the previous videos. + +This mixin takes care of the hassle of managing a ticker. Just slap it onto your widget’s state and now your state is secretly a `TickerProvider`. + +``` +**class** _MyWidgetState **extends** State<MyWidget> + **with** SingleTickerProviderStateMixin<MyWidget> { + + @override + Widget build(BuildContext context) { + return Container(); + } +} +``` + + +What this means is that the Flutter framework can ask your state for a ticker. Most important, `AnimationController` can ask the state for a ticker. + +``` +**class** _MyWidgetState **extends** State<MyWidget> + **with** SingleTickerProviderStateMixin<MyWidget> { + AnimationController **_controller**; + + @override + void initState() { + super.initState(); + _controller = AnimationController(vsync: this); + } + + @override + Widget build(BuildContext context) { + return Container(); + } +} +``` + + +`AnimationController` *needs* a ticker for it to function. If you use `SingleTickerProviderStateMixin` or its cousin `TickerProviderStateMixin`, you can just give `this` to the `AnimationController`, and you’re done. + +## AnimationController + +`AnimationController` is what you normally use to play, pause, reverse, and stop animations. Instead of pure “tick” events, `AnimationController` tells us at which *point* of the animation we are, at any time. For example, are we halfway there? Are we 99% there? Have we completed the animation? + +<DashImage figure src="images/1nKjFR7DVd-2r7_sSgBprfA.webp" /> + + +Normally, you take the `AnimationController`, maybe transform it with a `Curve`, put it through a `Tween`, and use it in one of the handy widgets like `FadeTransition` or `TweenAnimationBuilder`. But, for educational purposes, let’s not do that. Instead, we will directly call `setState`. + +## setState + +After we initialize the `AnimationController`, we can add a listener to it. And, in that listener, we call `setState`. + +``` +**class** _MyWidgetState **extends** State<MyWidget> + **with** SingleTickerProviderStateMixin<MyWidget> { + AnimationController **_controller**; + + @override + void initState() { + super.initState(); + _controller = AnimationController(vsync: this); + _controller.addListener(_update); + } + + void _update() { + setState(() { + // TODO + }); + } + + @override + Widget build(BuildContext context) { + return Container(); + } +} +``` + + +Now, we should probably have a state to set. Let’s keep it simple with an integer. And let’s not forget to actually use the state in our build method, and to change the state in our listener according to the current value of the controller. + +``` +**class** _MyWidgetState **extends** State<MyWidget> + **with** SingleTickerProviderStateMixin<MyWidget> { + AnimationController **_controller**; + + int i = 0; + + @override + void initState() { + super.initState(); + _controller = AnimationController(vsync: this); + _controller.addListener(_update); + } + + void _update() { + setState(() { + i = (_controller.value * 299792458).round(); + }); + } + + @override + Widget build(BuildContext context) { + return Text('$i m/s'); + } +} +``` + + +This code assigns a value from zero to the speed of light depending on the animation’s progress. + +## Running the animation + +Now, we just need to tell the animation how long it should take to complete, and start the animation. + +``` +**class** _MyWidgetState **extends** State<MyWidget> + **with** SingleTickerProviderStateMixin<MyWidget> { + AnimationController **_controller**; + + int i = 0; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, + duration: const Duration(seconds: 1), + ); + _controller.addListener(_update); + _controller.forward(); + } + + void _update() { + setState(() { + i = (_controller.value * 299792458).round(); + }); + } + + @override + Widget build(BuildContext context) { + return Text('$i m/s'); + } +} +``` + + +The widget animates as soon as it’s added to the screen. And it “animates” from zero to the speed of light in a second. + +<DashImage figure src="images/1uuyMl4qHr_bFZJENdMDimQ.webp" /> + + +## Disposing of the controller + +Oh, and don’t forget to dispose of the `AnimationController`. Otherwise you have a memory leak in your app. + +``` +**class** _MyWidgetState **extends** State<MyWidget> + **with** SingleTickerProviderStateMixin<MyWidget> { + AnimationController **_controller**; + + int i = 0; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, + duration: const Duration(seconds: 1), + ); + _controller.addListener(_update); + _controller.forward(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + void _update() { + setState(() { + i = (_controller.value * 299792458).round(); + }); + } + + @override + Widget build(BuildContext context) { + return Text('$i m/s'); + } +} +``` + + +## Just use a built-in widget, maybe? + +As you can see, doing it all by yourself is not great. The same functionality can be achieved with the `TweenAnimationBuilder` in much fewer lines of code, and without having to juggle an `AnimationController` and calling `setState`. + +``` +**class** MyPragmaticWidget **extends** StatelessWidget { + @override + Widget build(BuildContext context) { + **return** TweenAnimationBuilder( + tween: IntTween(begin: 0, end: 299792458), + duration: **const** Duration(seconds: 1), + builder: (BuildContext context, int i, Widget child) { + **return** Text(**'**$i m/s**'**); + }, + ); + } +} +``` + + +## Summary + +We saw what `Ticker` really is. We saw how to manually listen to an `AnimationController`. And, we saw that, at the basic level, animations are just fast, consecutive rebuilds of a widget. You can do whatever you want on any frame. + +Articles in this series: + +* [How to Choose Which Flutter Animation Widget is Right for You?](https://medium.com/p/79ecfb7e72b5/edit) + +* [Flutter animation basics with implicit animations](https://medium.com/flutter/flutter-animation-basics-with-implicit-animations-95db481c5916) + +* [Custom Implicit Animations in Flutter…with TweenAnimationBuilder](https://medium.com/flutter/custom-implicit-animations-in-flutter-with-tweenanimationbuilder-c76540b47185) + +* [Directional animations with built-in explicit animations](https://medium.com/flutter/directional-animations-with-built-in-explicit-animations-3e7c5e6fbbd7) + +* [When should I useAnimatedBuilder or AnimatedWidget?](https://medium.com/flutter/when-should-i-useanimatedbuilder-or-animatedwidget-57ecae0959e8) + +* [Animation deep dive](https://medium.com/flutter/animation-deep-dive-39d3ffea111f) (this article) \ No newline at end of file diff --git a/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/10GLFE1-zEg0xX0OnOXhcuw.webp b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/10GLFE1-zEg0xX0OnOXhcuw.webp new file mode 100644 index 0000000000..c1bbeb1c7d Binary files /dev/null and b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/10GLFE1-zEg0xX0OnOXhcuw.webp differ diff --git a/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/10gdFpYTSo_TTCqd-XVcvYw.webp b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/10gdFpYTSo_TTCqd-XVcvYw.webp new file mode 100644 index 0000000000..802ddfa6df Binary files /dev/null and b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/10gdFpYTSo_TTCqd-XVcvYw.webp differ diff --git a/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/17qStCx_t14Hw6EQcazYcKg.webp b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/17qStCx_t14Hw6EQcazYcKg.webp new file mode 100644 index 0000000000..ddc1a6fd58 Binary files /dev/null and b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/17qStCx_t14Hw6EQcazYcKg.webp differ diff --git a/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/18rAC3V333Gf0okK8dZ7T0g.webp b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/18rAC3V333Gf0okK8dZ7T0g.webp new file mode 100644 index 0000000000..87a66559d5 Binary files /dev/null and b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/18rAC3V333Gf0okK8dZ7T0g.webp differ diff --git a/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/1FQAuiQXKSnCRxGLnAIK40g.webp b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/1FQAuiQXKSnCRxGLnAIK40g.webp new file mode 100644 index 0000000000..2447509261 Binary files /dev/null and b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/1FQAuiQXKSnCRxGLnAIK40g.webp differ diff --git a/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/1K_NpCWpLtmotANb-PihiaA.webp b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/1K_NpCWpLtmotANb-PihiaA.webp new file mode 100644 index 0000000000..3a92a8ed97 Binary files /dev/null and b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/1K_NpCWpLtmotANb-PihiaA.webp differ diff --git a/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/1nx_Zc1SySvxMP5k8mR_a-g.webp b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/1nx_Zc1SySvxMP5k8mR_a-g.webp new file mode 100644 index 0000000000..605f802921 Binary files /dev/null and b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/1nx_Zc1SySvxMP5k8mR_a-g.webp differ diff --git a/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/1ph2_G0FsgvpK8nUxwQpxgA.webp b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/1ph2_G0FsgvpK8nUxwQpxgA.webp new file mode 100644 index 0000000000..a6bca6fc80 Binary files /dev/null and b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/1ph2_G0FsgvpK8nUxwQpxgA.webp differ diff --git a/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/1yvV7vhMImTkHfS-g-638IA.gif b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/1yvV7vhMImTkHfS-g-638IA.gif new file mode 100644 index 0000000000..7fe14fd133 Binary files /dev/null and b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/1yvV7vhMImTkHfS-g-638IA.gif differ diff --git a/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/1znPAjWvnhngSihYnlzQmgA.webp b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/1znPAjWvnhngSihYnlzQmgA.webp new file mode 100644 index 0000000000..857e571448 Binary files /dev/null and b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/images/1znPAjWvnhngSihYnlzQmgA.webp differ diff --git a/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/index.md b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/index.md new file mode 100644 index 0000000000..523a51416e --- /dev/null +++ b/sites/www/content/blog/animation-management-with-flutter-and-flux-redux/index.md @@ -0,0 +1,212 @@ +--- +title: "Animation Management with Flutter and Flux/Redux" +description: "The Flux and Redux application architectures work well with reactive view frameworks like Flutter. Both architectures share two powerful…" +publishDate: 2018-01-19 +author: amir_h +image: images/10gdFpYTSo_TTCqd-XVcvYw.webp +category: spotlight +layout: blog +--- + +The [Flux](https://facebook.github.io/flux/) and [Redux](https://redux.js.org/) application architectures work well with reactive view frameworks like Flutter. Both architectures share two powerful notions: creating a unidirectional data flow in an application, and a clean separation between business and presentation logic. + +It’s not always clear where to fit animation state management within these architectures. This is a proposal for a simple way to incorporate animation logic within Redux or Flux applications. + +This is an expanded version of a lightning talk I gave at DartConf 2018: [Animation Management with Flutter and Redux (YouTube)](https://www.youtube.com/watch?v=9ZkLtr0Fbgk&index=22&list=PLOU2XLYxmsIIJr3vjxggY7yGcGO7i9BK5). + +<DashImage figure src="images/10gdFpYTSo_TTCqd-XVcvYw.webp" alt="An animation conflict resolved with a “jump cut” effect." caption="An animation conflict resolved with a “jump cut” effect." /> + + +## The 2048 game + +For purposes of this discussion, I’m going to explain the approach by walking through an example implementation of the 2048 game using [Flutter](https://flutter.io/) and a Redux architecture. + +(In case you’re not familiar with the game, [try this web based version](https://gabrielecirulli.github.io/2048/).) + +## Building 2048 with Flutter and Redux (no animations) + +The example uses a typical Redux data flow: + +<DashImage figure src="images/1K_NpCWpLtmotANb-PihiaA.webp" /> + + +The data flows as follows: + +1. The view responds to user events by dispatching actions to the Redux store. + +1. The store computes new application states in response to actions, and publishes the states. + +1. The published states are consumed by the view logic, which updates the screen. + +The game state is defined with a 4x4 integer matrix (a list of lists in [Dart](https://www.dartlang.org/)). Zeros represent empty cells, and integer values represent each tile: + +``` +[0, 0, 0, 2], +[0, 0, 0, 4], +[0, 0, 0, 0], +[0, 0, 0, 0], +``` + + +Represents: + +<DashImage figure src="images/1znPAjWvnhngSihYnlzQmgA.webp" /> + + +The game state supports four operations: moveLeft, moveRight, moveUp, and moveDown. + +Here’s an example unit test for the moveRight operation: + +```dart + test('move right', () { + BoardState state = new BoardState([ + [0, 2, 0, 0], + [0, 2, 0, 2], + [8, 0, 4, 4], + [2, 2, 0, 2], + ]); + + expect(state.moveRight().tiles, [ + [0, 0, 0, 2], + [0, 0, 0, 4], + [0, 0, 8, 8], + [0, 0, 2, 4], + ]); + }); +``` + +The user moves the tiles with a drag gesture. The UI code sets up a gesture detector and responds to drag events by dispatching actions to the store. The gesture detector is defined as follows: + +```dart + return new GestureDetector( + behavior: HitTestBehavior.opaque, + onHorizontalDragEnd: (DragEndDetails d) { + if (d.primaryVelocity > 0) { + GameRedux.dispatch(context, moveRight()); + } else { + GameRedux.dispatch(context, moveLeft()); + } + }, + //... +``` + +Finally, the UI must update the screen each time the game state changes. Without introducing animations, this is straightforward — the UI subscribes to store updates, and rebuilds the Flutter Widget tree every time the store publishes a new state. + +So far we have maintained a clear separation of UI logic (mapping an application state to a Widget tree, and event handlers that dispatch actions), and business logic (the store). + +## Adding transition animations + +The 2048 game is not the same without animations, and we want to add them: + +<DashImage figure src="images/1yvV7vhMImTkHfS-g-638IA.gif" /> + + +It is not always clear where to keep animation logic within a Redux architecture. So far, our sample game has a clear separation of business logic and UI logic, but where to put the animation code? + +One possible approach tracks the intermediate position of each tile as part of the application state in the store. This essentially makes it part of the business logic. + +Another approach, and the one I prefer in this case, places this animation (as well as all “cosmetic” animations) in the presentation layer. This simplifies the state graph and our business logic. + +## “Cosmetic” and “real” transition states + +We make a distinction between 2 kinds of transition states: + +* **Real** **transition state** +The application waits for an external event (other than an animation tick). For example, a file loads, or the camera lens moves into position. While transitioning (the file is loading, camera lens is moving), the application must wait, so the user must also wait. + +* **Cosmetic transition state** +The application synthetically makes something appear to take longer than it really does. For example, animating the sliding tiles is a cosmetic transition — nothing prevents the application from immediately processing the next event as if the transition was already completed. Cosmetic transitions are typically used to make the user experience smoother. + +A few more notes about cosmetic transitions: + +* They are typically short, as we want our application to feel responsive, and we don’t want to introduce unnecessary (or annoying) long delays. + +* They are commonly used in mobile apps and, in our experience, can result in bugs due to transition conflicts (more on this below). + +* Their purpose is to create a more delightful UX, so we prefer to keep them out of the business logic layer. + +## “Fire-and-forget” animations + +In order to run an animation, its state (progress) must be tracked somewhere. We often use fire-and-forget abstractions with animations, basically delegating the animation state management to other code. A downside of “fire-and-forget” is that we limit the visibility and control that we have over the animation progress (and thus limit the flexibility of what can be done while the animation is running). The upside is that it keeps the calling code simpler (no need to manage the animation state), and delegating animation state management makes it possible for frameworks to optimize performance. For example, on Android some fire-and-forget animations are executed on the RenderThread. + +## Figuring out whether/which transition to show + +Our example keeps the animation logic in the UI layer, which rebuilds the widget tree in response to state updates. + +The first problem we encounter is that the store update which only includes the last state is not enough information for figuring out which transition to show. + +For example when the UI receives a store update with the following state: + +``` +[0, 0, 0, 2], +[0, 0, 0, 4], +[0, 0, 0, 0], +[0, 0, 0, 0] +``` + + +Given only this information, it’s possible to show any of the following transitions: + +<DashImage figure src="images/18rAC3V333Gf0okK8dZ7T0g.webp" /> + + +<DashImage figure src="images/1FQAuiQXKSnCRxGLnAIK40g.webp" /> + + +<DashImage figure src="images/1nx_Zc1SySvxMP5k8mR_a-g.webp" /> + + +To deal with this ambiguity, we provide more information with our store updates: In addition to the new state, our store update includes the previous state, and the action that triggered the update. This triplet is enough to figure which transition we should trigger (*): + +<DashImage figure src="images/1ph2_G0FsgvpK8nUxwQpxgA.webp" /> + + +<DashImage figure src="images/17qStCx_t14Hw6EQcazYcKg.webp" /> + + +* With the assumption that we are starting from a non transitioning state; see more on this below. + +## Transition conflicts + +One thing we need to take into account is that the user might trigger events while an animation is in progress. For example, while the tiles are sliding to the right, the user might swipe up, interrupting the first animation. + +Transition conflicts are a common cause of application bugs. Sometimes, animations conflict (even when they are presumably unrelated to each other), leaving the UI in an inconsistent state. These bugs can be a pain to diagnose: a user reports that the UI is in an inconsistent state, and he’s not sure how to reproduce it (which usually ends up being something like “tap button A and quickly tap button B”). + +Sometimes these bugs are not easy to fix and developers resort to a quick-and-dirty solution, such as “disable button A while the animation is running”, which can introduce more subtle bugs. One example of such a bug that was made public is the [iPhone’s calculator “1+2+3” bug](https://qz.com/1114019/if-you-type-123-into-your-apple-iphones-calculator-on-ios-11-you-probably-wont-get-6-aapl/). (Have you ever dealt with an animation conflict bug that you can publicly share? I’d love to hear about it in the comments section!*)* + +## Dealing with transition conflicts + +The animations in the 2048 game are “cosmetic” transitions — they are short, and shouldn’t delay the user from performing the next action. Also, the business logic layer does not know about these transitions. At any given point, the UI can trigger an action (moveLeft/moveRight/…) and the store publishes a new state to the UI. + +This is how the UI code responds to state updates: + +1. Terminate any ongoing transitions. + +1. Compute the next transition based on the (previousState, action, state) triplet published with the store update. + +1. “Fire-and-forget” the transition animation. (*) + +Let’s see what happens when a transition conflict occurs. In the following screen recording I swipe to the left and, while the tiles are sliding to the left, I swipe down (the white circles are tap indicators): + +<DashImage figure src="images/10GLFE1-zEg0xX0OnOXhcuw.webp" /> + + +This results in a [jump cut](https://en.wikipedia.org/wiki/Jump_cut) effect: the moveLeft animation immediately completes, and the moveDown animation starts. + +While not always the ideal way to resolve animation conflicts, I argue that in this case and many other cases of cosmetic transition conflicts, the jump cut is an acceptable solution. Cosmetic transition animations are typically short (the animation has been artificially slowed in the previous recording), which make the jump cut feel natural and responsive. + +We get this default conflict resolution “for free” as long as we make sure that our UI keeps this contract: *Given that the last store update was (stateA, action, stateB) the UI is either showing stateB or showing a transition to stateB.* If you’re using Flutter, this is probably what your UI is already doing. + +Note that our UI code is performing minimal and simple state management — it just maintains the ability to terminate an ongoing transition. + +* Not exactly “fire-and-forget”, as we need to provide a way to terminate the animation. + +## A more graceful animation conflict resolution + +For components where the jump cut effect isn’t sufficient, you can manage more of the animation state in the UI layer, and provide custom handling for transition conflicts. This approach doesn’t help with custom animation conflict resolution, but neither does it make it harder. The upside of this approach is that you use custom handling where you want it, and other conflicts default to the jump cut effect. + +## Summary + +In this article, we implemented the 2048 game with a simple Redux architecture, keeping a clean separation between business and presentation logic. We kept state management for cosmetic transitions out of the business logic. Finally, we managed to keep minimal state management in the UI layer, and we resolved animation conflict in a simple and reasonable way. + +You can find the (not polished!) [2048 example code](https://github.com/amirh/indux/tree/master/examples/2048) on GitHub. \ No newline at end of file diff --git a/sites/www/content/blog/announcing-adobe-xd-support-for-flutter/images/0ADItKKHZGWwawDHa.webp b/sites/www/content/blog/announcing-adobe-xd-support-for-flutter/images/0ADItKKHZGWwawDHa.webp new file mode 100644 index 0000000000..1988ea281c Binary files /dev/null and b/sites/www/content/blog/announcing-adobe-xd-support-for-flutter/images/0ADItKKHZGWwawDHa.webp differ diff --git a/sites/www/content/blog/announcing-adobe-xd-support-for-flutter/images/0N-K68XqE0DBUV0EK.webp b/sites/www/content/blog/announcing-adobe-xd-support-for-flutter/images/0N-K68XqE0DBUV0EK.webp new file mode 100644 index 0000000000..807112d996 Binary files /dev/null and b/sites/www/content/blog/announcing-adobe-xd-support-for-flutter/images/0N-K68XqE0DBUV0EK.webp differ diff --git a/sites/www/content/blog/announcing-adobe-xd-support-for-flutter/images/0Vjx2GfxTsr8Imvew.webp b/sites/www/content/blog/announcing-adobe-xd-support-for-flutter/images/0Vjx2GfxTsr8Imvew.webp new file mode 100644 index 0000000000..d754df21bb Binary files /dev/null and b/sites/www/content/blog/announcing-adobe-xd-support-for-flutter/images/0Vjx2GfxTsr8Imvew.webp differ diff --git a/sites/www/content/blog/announcing-adobe-xd-support-for-flutter/images/0llO5f0NhDCAp5Y7j.webp b/sites/www/content/blog/announcing-adobe-xd-support-for-flutter/images/0llO5f0NhDCAp5Y7j.webp new file mode 100644 index 0000000000..4f3f35ce91 Binary files /dev/null and b/sites/www/content/blog/announcing-adobe-xd-support-for-flutter/images/0llO5f0NhDCAp5Y7j.webp differ diff --git a/sites/www/content/blog/announcing-adobe-xd-support-for-flutter/index.md b/sites/www/content/blog/announcing-adobe-xd-support-for-flutter/index.md new file mode 100644 index 0000000000..11d07ea0d9 --- /dev/null +++ b/sites/www/content/blog/announcing-adobe-xd-support-for-flutter/index.md @@ -0,0 +1,49 @@ +--- +title: "Announcing Adobe XD support for Flutter" +description: "Create in XD and export to working Flutter code" +publishDate: 2020-05-13 +author: timsneath +image: images/0N-K68XqE0DBUV0EK.webp +category: spotlight +layout: blog +--- + +Our goal with Flutter is to provide a rich canvas for creative expression. With native performance on iOS and Android, full control over every pixel rendered on the screen, and the ability to iterate rapidly with stateful hot reload, we want to unleash the potential of designers and developers to build beautiful experiences that aren’t limited by artificial technical boundaries. + +Last year at our [Flutter Interact event](https://www.youtube.com/watch?v=HjZxyTJzvYg&feature=emb_logo), we turned the spotlight on creators who are using Flutter to explore and experiment. We heard from digital artists like [Robert Felker](https://www.youtube.com/watch?v=DEppSs_ko48), who uses Flutter to build ethereal forms with generative algorithms. We presented [the work of creative agencies like gskinner](https://flutter.gskinner.com/), who created a series of innovative vignettes to demonstrate the potential of Flutter. And we saw a demonstration from Adobe of an [early prototype of a plugin for Adobe XD](https://www.youtube.com/watch?v=ukLBCRBlIkk&feature=youtu.be&t=3652) that exports Flutter code directly from their tool. + +<DashImage figure src="images/0llO5f0NhDCAp5Y7j.webp" alt="Flutter is a rich canvas for creative expression: a toolkit for beautiful, native experiences that aren’t limited by traditional technical boundaries. (Generative artwork built with Flutter, courtesy of Robert Felker.)" caption="Flutter is a rich canvas for creative expression: a toolkit for beautiful, native experiences that aren’t limited by traditional technical boundaries. (Generative artwork built with Flutter, courtesy of Robert Felker.)" /> + + +Today, **we’re delighted to join Adobe in announcing that their XD to Flutter plugin is [now available as early access](https://adobe.com/go/xd_plugins_discover_plugin?pluginId=6eaf77ea)** for broader public testing. [Adobe XD](https://www.adobe.com/products/xd.html) is a UI/UX design and collaboration tool that helps teams create and share designs for websites, apps, voice interfaces, games, and more. Part of Adobe’s class-leading [Creative Cloud](https://www.adobe.com/creativecloud.html), XD allows designers to turn vector art, text, images, microinteractions, and animations into interactive prototypes that behave like working software products. The ability to export designs to Flutter further reduces the latency between creative ideas and product development, as an XD prototype can now become working Flutter code within minutes. Adobe XD supports design on Windows or macOS, and includes a [free starter plan](https://www.adobe.com/products/xd/compare-plans.html) to get you up and running. + +<DashImage figure src="images/0Vjx2GfxTsr8Imvew.webp" alt="Exporting from Adobe XD to Flutter is now just a plugin away." caption="Exporting from Adobe XD to Flutter is now just a plugin away." /> + + +## Exporting Flutter code from Adobe XD + +Using the Flutter plugin in XD is straightforward. You can either export an individual drawing or component, or an artboard. Here’s how. + +Start by installing the [Flutter exporter plugin](https://adobe.com/go/xd_plugins_discover_plugin?pluginId=6eaf77ea). From Adobe XD, choose *Plugins > Discover Plugins*, and search for Flutter. Once you’ve installed it, you can display the UI Panel shown in the screenshot above by choosing *Plugins > Flutter > UI Panel*. + +Now add the [adobe_xd](https://pub.dev/packages/adobe_xd) package to your Flutter project by including it in your `pubspec.yaml` app manifest. This package provides helper functions to minimize boilerplate in the generated XD code. + +To export a single element, simply select the individual widget you’d like to export to Flutter, and choose the *Copy Selected* button from the UI panel. This copies the relevant Dart code to your clipboard, which you can use as the basis for a stateless or stateful widget: + +<DashImage figure src="images/0ADItKKHZGWwawDHa.webp" alt="Exported code can be integrated into your existing project and updated without reworking other files." caption="Exported code can be integrated into your existing project and updated without reworking other files." /> + + +Another approach is to export the entire project. Assuming you’ve already got a Flutter app that you want to load the content into (including the `adobe_xd` package reference in `pubspec.yaml`), you can simply choose *Plugins > Flutter > Export All Widgets* from the UI panel, and set any additional configuration options you want. + +This creates a series of classes in the `lib/` subdirectory of your project, which you can then use directly. You can continue to tweak the XD prototype and export again with ⇧⌘F (Ctrl+Shift+F on Windows) and, if you have enabled the Dart [*Hot Reload on Save Watcher* setting](https://dartcode.org/docs/settings/#dartpreviewhotreloadonsavewatcher) in Visual Studio Code, your app automatically reloads with any updates when you re-export the widgets. + +<DashImage figure src="images/0N-K68XqE0DBUV0EK.webp" alt="Having a fast turnaround from XD to code gives you a new way to turn prototypes into apps." caption="Having a fast turnaround from XD to code gives you a new way to turn prototypes into apps." /> + + +As an early access preview, there are of course some limitations, which are described in the [release notes](https://github.com/AdobeXD/xd-to-flutter-plugin/blob/master/README.md). One notable limitation is that responsive layout is not yet available, pending completion of a new XD API. But you’ll automatically get updates to the plugin as new features like this become available. + +It’s been great working with Adobe on this. We’re thrilled to have them supporting Flutter, and the [new JavaScript-based APIs](https://adobexdplatform.com/plugin-docs/) make extending Adobe XD easy for everyone. Here’s what they have to say about this new plugin: +> *“At Adobe, we’re always looking to simplify the designer-to-developer workflow that pains so many teams designing and building apps. Today, we’re excited to release an early access preview of the work that’s come out of our partnership with Flutter to remove guesswork, accelerate decision making, and help teams bring new experiences to market faster.”* +> - Vijay Vachani, Senior Director of Creative Cloud Platform & Ecosystem, Adobe + +For more information, visit [Adobe’s page for the XD to Flutter plugin](https://github.com/AdobeXD/xd-to-flutter-plugin). We’re looking forward to seeing what you create with it! \ No newline at end of file diff --git a/sites/www/content/blog/announcing-codepen-support-for-flutter/images/06wPqdCz_TzB2gAXr.webp b/sites/www/content/blog/announcing-codepen-support-for-flutter/images/06wPqdCz_TzB2gAXr.webp new file mode 100644 index 0000000000..3ce321854f Binary files /dev/null and b/sites/www/content/blog/announcing-codepen-support-for-flutter/images/06wPqdCz_TzB2gAXr.webp differ diff --git a/sites/www/content/blog/announcing-codepen-support-for-flutter/images/0bPcKdz1gG_1UxR6D.webp b/sites/www/content/blog/announcing-codepen-support-for-flutter/images/0bPcKdz1gG_1UxR6D.webp new file mode 100644 index 0000000000..618143c497 Binary files /dev/null and b/sites/www/content/blog/announcing-codepen-support-for-flutter/images/0bPcKdz1gG_1UxR6D.webp differ diff --git a/sites/www/content/blog/announcing-codepen-support-for-flutter/images/0ulpFtxXC_93Qp59b.webp b/sites/www/content/blog/announcing-codepen-support-for-flutter/images/0ulpFtxXC_93Qp59b.webp new file mode 100644 index 0000000000..3d9a2a5bb4 Binary files /dev/null and b/sites/www/content/blog/announcing-codepen-support-for-flutter/images/0ulpFtxXC_93Qp59b.webp differ diff --git a/sites/www/content/blog/announcing-codepen-support-for-flutter/images/0zfGvzw1Zb8SwDcpv.webp b/sites/www/content/blog/announcing-codepen-support-for-flutter/images/0zfGvzw1Zb8SwDcpv.webp new file mode 100644 index 0000000000..09590a8435 Binary files /dev/null and b/sites/www/content/blog/announcing-codepen-support-for-flutter/images/0zfGvzw1Zb8SwDcpv.webp differ diff --git a/sites/www/content/blog/announcing-codepen-support-for-flutter/images/111R_BtjzdSuPel44qDByKA.gif b/sites/www/content/blog/announcing-codepen-support-for-flutter/images/111R_BtjzdSuPel44qDByKA.gif new file mode 100644 index 0000000000..fa84e814e1 Binary files /dev/null and b/sites/www/content/blog/announcing-codepen-support-for-flutter/images/111R_BtjzdSuPel44qDByKA.gif differ diff --git a/sites/www/content/blog/announcing-codepen-support-for-flutter/images/1YdadfEdp4pALMekmxaPWFA.webp b/sites/www/content/blog/announcing-codepen-support-for-flutter/images/1YdadfEdp4pALMekmxaPWFA.webp new file mode 100644 index 0000000000..2477aef585 Binary files /dev/null and b/sites/www/content/blog/announcing-codepen-support-for-flutter/images/1YdadfEdp4pALMekmxaPWFA.webp differ diff --git a/sites/www/content/blog/announcing-codepen-support-for-flutter/images/1uEJNcnBPZRxMr1EK0aqBRw.webp b/sites/www/content/blog/announcing-codepen-support-for-flutter/images/1uEJNcnBPZRxMr1EK0aqBRw.webp new file mode 100644 index 0000000000..73f5352cf8 Binary files /dev/null and b/sites/www/content/blog/announcing-codepen-support-for-flutter/images/1uEJNcnBPZRxMr1EK0aqBRw.webp differ diff --git a/sites/www/content/blog/announcing-codepen-support-for-flutter/index.md b/sites/www/content/blog/announcing-codepen-support-for-flutter/index.md new file mode 100644 index 0000000000..9255343a6a --- /dev/null +++ b/sites/www/content/blog/announcing-codepen-support-for-flutter/index.md @@ -0,0 +1,59 @@ +--- +title: "Announcing CodePen support for Flutter" +description: "Today we’re excited to announce that CodePen, the leading social development environment for millions of front-end developers and…" +publishDate: 2020-04-15 +author: zoeyfan +image: images/06wPqdCz_TzB2gAXr.webp +category: spotlight +layout: blog +--- + +<DashImage figure src="images/06wPqdCz_TzB2gAXr.webp" /> + + +Today we’re excited to announce that [CodePen](http://codepen.io), the leading social development environment for millions of front-end developers and designers, is adding [support for Flutter](https://codepen.io/flutter)! For web developers CodePen has long been a great place for sharing design explorations, new techniques, and ideas. Now with the introduction of Flutter, CodePen enables a new audience to learn, share and promote their creativity. + +In the words of Alex Vazquez, one of the co-founders of CodePen: +> “CodePen has been a big fan of Flutter and the Dart programming language for years. We’re pretty pumped that Flutter now supports mobile *and* the web. The Flutter community is growing fast, which is why we’re excited to support Flutter with its own custom CodePen editor! Flutter is officially a first-class member of the CodePen community. We can’t wait to see what you build with Flutter on CodePen.” + +Since the inception of Flutter, we’ve designed it as a canvas for creative expression. It has been encouraging to see growing recognition of its design capabilities, from the partnerships we announced with Adobe and SuperNova at our design-orientated [Flutter Interact event](http://g.co/FlutterInteract), to the naming of [Flutter as one of the most important design ideas of the decade](https://www.fastcompany.com/90442092/the-14-most-important-design-ideas-of-the-decade-according-to-the-experts) by Fast Company. With the new addition of a CodePen-based Flutter environment, creative professionals can now leverage CodePen to discover artistic Flutter inspirations, build up impressive Flutter portfolios, and showcase amazing Flutter ideas to the world! + +CodePen’s Flutter editor is built on top of the same backend service, [dart-services](https://github.com/dart-lang/dart-services), that currently powers [DartPad](http://www.dartpad.dev) (the web-based editor created by the Flutter and Dart teams that has been recently updated to support Flutter). When we built DartPad, we designed it to be an educational tool to help developers learn Flutter and Dart, and share code snippets with each other. We specifically decided to open-source [dart-services](https://github.com/dart-lang/dart-services) so that sites like CodePen could tailor it to new scenarios and audiences. + +DartPad is an excellent tool for quickly testing an idea in code, or for sharing your snippet with other developers, and it’s useful for reproducing (and filing) bugs. You can do this in CodePen, too, but CodePen has the advantage of an active design community where you can share, comment, promote, try out design ideas, and get input from other designers. CodePen’s Flutter editor is more like your “right brain” that you use to prototype ideas for creative expression and design inspiration, while DartPad is more like your “left brain” when you need to quickly test an idea or code up a technical concept. + +<DashImage figure src="images/1uEJNcnBPZRxMr1EK0aqBRw.webp" alt="*[“Light effect” animation](https://codepen.io/zoeyfan/pen/mdeebvy) by [Mariano Zorrilla](https://gist.github.com/mkiisoft)*" caption="*[“Light effect” animation](https://codepen.io/zoeyfan/pen/mdeebvy) by [Mariano Zorrilla](https://gist.github.com/mkiisoft)*" /> + + +## Flutter editor on CodePen + +Let’s take a quick tour of the Flutter editor on CodePen. You can create a new Flutter pen (CodePen’s term for “code snippet”) either from [scratch](https://codepen.io/pen/editor/flutter) or from an existing [template](https://codepen.io/topic/flutter/templates). Many thanks to our esteemed Flutter community members ([@aednlaxer](https://github.com/aednlaxer), [@ayushnishad](https://github.com/ayushnishad), [@diegoveloper](https://github.com/diegoveloper/), [@divyanshub024](https://github.com/divyanshub024), [@egorbelibov](https://github.com/egorbelibov/), [@gskinnerTeam](https://github.com/gskinnerTeam), [@mkiisoft](https://gist.github.com/mkiisoft), [@orestesgaolin](https://github.com/orestesgaolin/), [@SlaxXxX](https://github.com/SlaxXxX), and others) for contributing some cool samples to the template list. + +Let’s start with the [“GooeyEdge” template](https://codepen.io/zoeyfan/pen/ExVaXGK). As you can see, the Flutter code is on the left side, and Flutter’s web output is on the right side. You can play with this interactive design by dragging the edge of the visual using your mouse. + +<DashImage figure src="images/111R_BtjzdSuPel44qDByKA.gif" alt="*[“Gooey edge” animation](https://codepen.io/zoeyfan/pen/ExVaXGK) by [Grant Skinner](https://github.com/gskinnerTeam)*" caption="*[“Gooey edge” animation](https://codepen.io/zoeyfan/pen/ExVaXGK) by [Grant Skinner](https://github.com/gskinnerTeam)*" /> + + +You can also make changes to the Flutter code, and see them take effect accordingly. For example, if we change the color of the page control indicator from “white” to “blue” (line 326), you can see the color gets updated within a few seconds! CodePen automatically recompiles your code each time you make a change. Just update a line, wait a few seconds, and the new output appears. + +<DashImage figure src="images/1YdadfEdp4pALMekmxaPWFA.webp" alt="Page control indicator changed into blue" caption="Page control indicator changed into blue" /> + + +Now let’s see what happens if I introduce a syntax error. Say I accidentally delete a semicolon at the end of line 1. I’ll immediately see a red bar warning of the syntax error. This allows you to easily spot and correct mistakes. + +<DashImage figure src="images/0bPcKdz1gG_1UxR6D.webp" alt="Warning message when introducing a syntax error" caption="Warning message when introducing a syntax error" /> + + +## Social features + +What we particularly love about CodePen is the social and community features it offers. Once you create a new pen or discover Flutter “pens” created by the community, you can save, favorite, add to a collection, share on social media, or even fork to create your own version! + +<DashImage figure src="images/0zfGvzw1Zb8SwDcpv.webp" /> + + +<DashImage figure src="images/0ulpFtxXC_93Qp59b.webp" /> + + +## Give it a try! + +We hope this new Flutter playground on [CodePen](https://codepen.io/flutter) empowers you to build and showcase your cool Flutter animations, ideas, vignettes, and more. Please share your designs with us on Twitter using **#FlutterPen**. We can’t wait to see what you build! If you missed last week’s announcement about [free training](https://medium.com/flutter/learn-flutter-for-free-c9bc3b898c4d), you might want to check that out. And keep an eye out: we’ll have more news very soon. \ No newline at end of file diff --git a/sites/www/content/blog/announcing-dart-null-safety-beta/images/0AwqXx58arqVt6PhL.webp b/sites/www/content/blog/announcing-dart-null-safety-beta/images/0AwqXx58arqVt6PhL.webp new file mode 100644 index 0000000000..a6fbe910e4 Binary files /dev/null and b/sites/www/content/blog/announcing-dart-null-safety-beta/images/0AwqXx58arqVt6PhL.webp differ diff --git a/sites/www/content/blog/announcing-dart-null-safety-beta/images/0dPlSded52Mt6P5tY.webp b/sites/www/content/blog/announcing-dart-null-safety-beta/images/0dPlSded52Mt6P5tY.webp new file mode 100644 index 0000000000..2c26ee7b27 Binary files /dev/null and b/sites/www/content/blog/announcing-dart-null-safety-beta/images/0dPlSded52Mt6P5tY.webp differ diff --git a/sites/www/content/blog/announcing-dart-null-safety-beta/images/0fM8TG0L56zVeJVlG.webp b/sites/www/content/blog/announcing-dart-null-safety-beta/images/0fM8TG0L56zVeJVlG.webp new file mode 100644 index 0000000000..5c53a15d8c Binary files /dev/null and b/sites/www/content/blog/announcing-dart-null-safety-beta/images/0fM8TG0L56zVeJVlG.webp differ diff --git a/sites/www/content/blog/announcing-dart-null-safety-beta/images/0zrUdYSzsjWZGr7Or.webp b/sites/www/content/blog/announcing-dart-null-safety-beta/images/0zrUdYSzsjWZGr7Or.webp new file mode 100644 index 0000000000..9507df9d80 Binary files /dev/null and b/sites/www/content/blog/announcing-dart-null-safety-beta/images/0zrUdYSzsjWZGr7Or.webp differ diff --git a/sites/www/content/blog/announcing-dart-null-safety-beta/images/1P2ufPitLRaJRAD__KUfbvg.webp b/sites/www/content/blog/announcing-dart-null-safety-beta/images/1P2ufPitLRaJRAD__KUfbvg.webp new file mode 100644 index 0000000000..6d6453f807 Binary files /dev/null and b/sites/www/content/blog/announcing-dart-null-safety-beta/images/1P2ufPitLRaJRAD__KUfbvg.webp differ diff --git a/sites/www/content/blog/announcing-dart-null-safety-beta/images/1kr-aQjGvnhY0JDCx1CGzRg.webp b/sites/www/content/blog/announcing-dart-null-safety-beta/images/1kr-aQjGvnhY0JDCx1CGzRg.webp new file mode 100644 index 0000000000..5ce0dc1dda Binary files /dev/null and b/sites/www/content/blog/announcing-dart-null-safety-beta/images/1kr-aQjGvnhY0JDCx1CGzRg.webp differ diff --git a/sites/www/content/blog/announcing-dart-null-safety-beta/index.md b/sites/www/content/blog/announcing-dart-null-safety-beta/index.md new file mode 100644 index 0000000000..ccdd49c765 --- /dev/null +++ b/sites/www/content/blog/announcing-dart-null-safety-beta/index.md @@ -0,0 +1,183 @@ +--- +title: "Announcing Dart null safety beta" +description: "Beginning the journey to migrate packages to a sound, safer state" +publishDate: 2020-11-19 +author: mit-mit +image: images/1kr-aQjGvnhY0JDCx1CGzRg.webp +category: announcements +layout: blog +--- + +*Cross-posted here and on the [Dart blog](https://medium.com/dartlang).* + +Today we’re announcing that **sound null safety is available in beta** for Dart and Flutter. Null safety is our latest major productivity feature, intended to [help you avoid null errors](https://dart.dev/null-safety), a class of bugs that are often hard to spot. If you’re looking for a quick summary of why we’re excited about null safety, check out this new video: + +<YoutubeEmbed id="iYhOU9AuaFs" title="Null safety in Dart - Introduction" fullwidth="true"/> + + +With null safety graduating to beta, **it’s time to start community migration** of the thousands of packages available on [pub.dev](https://pub.dev/). We’ve migrated the Dart core libraries, the Flutter framework, and [more than 40](https://pub.dev/packages?q=&prerelease-null-safe=1) Dart and Flutter packages. With that we hope to see the community embrace null safety by migrating their packages. + +<DashImage figure src="images/1kr-aQjGvnhY0JDCx1CGzRg.webp" /> + + +With beta, we’re also starting the home stretch before shipping a stable version of the null safety feature. We hope that you’ll use the feature and let us know if parts of it can be improved, if UI messages can be made easier to understand, or if documentation can be made clearer. We’re **really looking forward to [your feedback](https://github.com/dart-lang/sdk/issues/new?title=Null%20safety%20feedback:%20[issue%20summary]&labels=NNBD&body=Describe%20the%20issue%20or%20potential%20improvement%20in%20detail%20here)**. + +## Opting in to null safety + +Before we discuss null safety migration, it’s important to repeat that (as stated in our [null safety principles](https://dart.dev/null-safety#null-safety-principles)) you’re in control of when to begin null safety adoption. Apps and packages will only run with null safety if their minimum [Dart SDK constraint](https://dart.dev/null-safety#enable-null-safety) is at least a Dart 2.12 prerelease: + +``` +environment: + sdk: "**>=2.12.0-0** <3.0.0" +``` + + +To experience this, try to create a small null-safe hello app (for example, using `dart create`) containing code like shown below. You can then try to run the app both before and after changing the SDK constraint and running `dart pub get`, and experience how the program behavior changes. (Make sure to use an SDK that reports 2.12 in `dart --version`.) + +``` +***bin/hello.dart:*** +... +void main() { + var hello = 'Hello Dart developers'; + if (someCondition) { + hello = null; + } + print(hello); +} + +Before changing the SDK constraint: +$ dart run + +null + +After changing the SDK constraint (and running dart pub get): +$ dart run + +bin/hello.dart:6:13: Error: Null can't be assigned to a variable of type 'String' because 'String' is not nullable. + +hello = null; + ^ +``` + + +## Migrating to null safety + +To migrate a package (or simple app) to null safety, follow these five steps, which are fully documented in the [migration guide](https://dart.dev/null-safety/migration-guide) on dart.dev. + +### Step 1: Check if your dependencies are ready + +We strongly recommend migrating code in order, with the leaves of the dependency graph being migrated first. For example, if C depends on B which depends on A, migrate A to null safety first, then B, then C. This order applies whether A, B, and C are libraries, packages, or apps. + +<DashImage figure src="images/0fM8TG0L56zVeJVlG.webp" /> + + +Why is the order important? Although you can make some progress migrating code before your dependencies migrate, you risk having to do a second migration pass if your dependencies change their APIs during their migration. If a few of your dependencies aren’t null safe, consider reaching out to the package publishers using the contact details listed for each package on pub.dev. + +### Verifying that dependencies are ready + +To verify whether your app or package is ready to begin migration, you can use `dart pub outdated` in null-safety mode. The example below shows that this app is ready to migrate if it upgrades its dependencies to the prerelease versions of `path`, `process`, and `pedantic` as listed in the **Resolvable** column. + +<DashImage figure src="images/0zrUdYSzsjWZGr7Or.webp" /> + + +If null safety support is available in minor new versions, you’ll see those in the **Upgradable** column. Often null safety support will be available in major new versions; in that case you’ll see the versions listed under **Resolvable** in the outdated output. To upgrade to those, edit your `pubspec.yaml` file to allow those major versions. For example, you might change +`process: ^3.0.13` to `process: ^4.0.0-nullsafety`. + +You can also find packages with null safety support on pub.dev using the new **Null safety** tags on package pages (for example [collection 1.15](https://pub.dev/packages/collection/versions/1.15.0-nullsafety.5)) and a new **Advanced** **search** [null safety search option](https://pub.dev/packages?q=&prerelease-null-safe=1). + +<DashImage figure src="images/1P2ufPitLRaJRAD__KUfbvg.webp" /> + + +### Step 2: Migrate using the migration tool + +If your dependencies are ready, you can proceed to migrating your app or package using the migration tool, `dart migrate`. + +The migration tool is interactive, so you can review the nullability properties that the tool has inferred. If you disagree with any of the tool’s conclusions, you can add nullability hints to change the inference. Adding a few migration hints can have a huge impact on migration quality. + +<DashImage figure src="images/0dPlSded52Mt6P5tY.webp" /> + + +We’ve had a small number of Dart package authors test-drive migration using early preview builds of null safety, and their feedback has been encouraging. The migration guide has additional tips on how to use the migration tool. + +### Step 3: Statically analyze your migrated code + +Update your packages using `pub get` in your IDE or on the command line. Then use your IDE or the command line to perform static analysis on your Dart code: + +``` +$ dart pub get +$ dart analyze +``` + + +Or on your Flutter code: + +``` +$ flutter pub get +$ flutter analyze +``` + + +### Step 4: Ensure tests pass + +Run your tests and make sure that they pass. You might need to update tests that expect null values, in case you changed your package code to no longer allow nulls. + +### Step 5: Publish your null-safe package + +When the migration is complete and tests are passing, you can publish your package as a prerelease. Here’s a brief summary of best practices: + +* Increment your version number to the next major version (for example, `2.3.x` to `3.0.0`). This best practice ensures that users of your package don’t upgrade to it before they’re ready to use null safety themselves, and it gives you the freedom to refactor your APIs to best leverage null safety. + +* Version and publish your package as a [prerelease version on pub.dev](https://dart.dev/tools/pub/publishing#publishing-prereleases). (For example, use `3.0.0-nullsafety.0`, not `3.0.0`.) + +For full details on migration and versioning, see the [migration guide](https://dart.dev/null-safety/migration-guide). + +## The benefits of sound null safety + +Our previous blog posts for the technical previews of null safety [in Dart](https://medium.com/dartlang/announcing-sound-null-safety-defd2216a6f3) and [in Flutter](https://medium.com/flutter/null-safety-flutter-tech-preview-cb5c98aba187) have discussed the benefits of null safety via a number of examples. Now that null safety is nearing completion, we’re seeing several real-world examples of this benefit. + +### Safer code + +Just recently, we [discovered a bug](https://github.com/flutter/flutter/issues/69863) in the Flutter master channel where various `flutter` tool commands would crash on certain machine configurations with a null error: `The method '>=' was called on null`. The underlying issue was a recent pull request to add support for detecting Android Studio 4.1. That PR added code like this: + +``` +final int major = version?.major; +final int minor = version?.minor; +if (globals.platform.isMacOS) { + /// plugin path of Android Studio changed after version 4.1. + if (major >= 4 && minor >= 1) { + ... +``` + + +Can you spot the error? Because the version may be null, both major and minor may be null also. This bug may seem easy to spot here in isolation, but in practice code like this slips through all the time, even with a rigorous code review process like the one used in the Flutter repo. With null safety, static analysis [catches this issue immediately](https://dartpad.dev/0e9797be7488d8ec6c3fca92b7f2740f): + +<DashImage figure src="images/0AwqXx58arqVt6PhL.webp" /> + + +That was a pretty simple error. During our early use of null safety in code internally at Google, we’ve seen much more intricate errors be caught and then resolved via null safety. Here are a few examples: + +* An internal team found that they were often checking for null values in code that null safety knew could never be null. This problem was most frequently seen in code using [protobuf](https://developers.google.com/protocol-buffers), where optional fields return a default value when unset, and never null. This caused code to incorrectly check for the default condition, by confusing default values and null values. + +* The Google Pay team found bugs in their Flutter code where they would fail when trying to access Flutter `State` objects outside of the context of a `Widget`. Before null safety, those would return null and mask the error; with null safety, the sound analysis determined that those properties could never be null, and threw an analysis error. + +* The Flutter team found a bug where the Flutter engine could potentially crash if null was passed to the `scene` parameter in `Window.render()`. During null safety migration, they added a hint to [mark Scene as non-nullable](https://github.com/cbracken/engine/blob/bad869e229a8a02cad6e63d12e80807b33b5c12f/lib/ui/window.dart#L1069), and were then able to easily prevent potential app crashes that null would have triggered if passed. + +### Leveraging sound null safety during compilation + +The soundness of Dart’s null safety has another welcome implication: it means the Dart compilers can leverage the nullability information. This can potentially make your programs smaller and faster. We don’t have a lot of real-world apps fully migrated to sound null safety yet (as we’re just now kicking off the ecosystem migration of packages that these apps depend on for soundness), but we’re seeing very encouraging results from the core framework. + +We recently did a test recompilation of the [hello_world](https://github.com/flutter/flutter/blob/master/examples/hello_world/lib/main.dart) sample to measure the impact of null safety on app size.This is a minimal example that simply displays “hello world”. In [comparing ](https://gist.github.com/mit-mit/64e160f9dc3bf6c69c7ef2f81384594a)the overall size of the compiled code, the uncompressed (installed on device) code size shrank by 3.5% without doing anything but recompiling with sound null safety. This was possible, despite this app being just 10 lines of code, because the code size of all included libraries shrank; for example the Flutter framework itself (`package:flutter`) shrank by 3.9%. + +As for code speed, having to enforce a sound type system potentially adds overhead. However, having fewer null checks also potentially makes the code faster. Our initial analysis of benchmarks shows that performance is on par with previous releases, and that the new additional type information creates the potential for us to do new kinds of performance improvements in the future. We plan on writing more about our performance work in future blog posts. + +In some cases we’ve already seen null safety lead to performance gains, typically when migration to null safety has uncovered a flaw in the code logic. For example, we found an issue in the Flutter web text layout cache. This cache was using a key that was nullable, and then some logic to use `TextAlign.start` when null. This logic caused a flaw in the cache where elements would look like they had changed even though they still had a default value. As a result, there were frequent cache misses. Adding a non-nullable `textAlign` getter helped fix the caching flaw, resulting in a **14x increase** of text rendering performance in cases where the text is cached. + +## Get started today! + +Beta versions of Dart and Flutter that contain null safety are ready today. If you develop with Flutter, you can switch to beta with `flutter channel beta` followed by `flutter upgrade`. Otherwise, you can get the standalone Dart SDK from the [Dart SDK archive](https://dart.dev/tools/sdk/archive#beta-channel). + +If you develop packages, we encourage you to read our [migration guide](https://dart.dev/null-safety/migration-guide) and plan your migration. Please [let us know](https://github.com/dart-lang/sdk/issues/new?title=Null%20safety%20feedback:%20[issue%20summary]&labels=NNBD&body=Describe%20the%20issue%20or%20potential%20improvement%20in%20detail%20here) about any issues or suggestions you have. + +If you’re an app developer, you may prefer to delay migration until the feature is in our stable channels. We plan on addressing the feedback from beta quickly, fixing any remaining issues. It’s hard to state a concrete timeline for when null safety will be in a stable release, but we’re thinking early next year. + +Thanks for your support and feedback as we work to make Dart a more robust language, and Flutter a stronger framework! \ No newline at end of file diff --git a/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/00IEDbMjBcAYcEayr.webp b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/00IEDbMjBcAYcEayr.webp new file mode 100644 index 0000000000..cd40135daa Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/00IEDbMjBcAYcEayr.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/02cmIipg-JivBAlRW.webp b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/02cmIipg-JivBAlRW.webp new file mode 100644 index 0000000000..cc338cf9f9 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/02cmIipg-JivBAlRW.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/08Q2gB989P4yclIqD.webp b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/08Q2gB989P4yclIqD.webp new file mode 100644 index 0000000000..5f82d71d00 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/08Q2gB989P4yclIqD.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0ArEfmlujiPiVUD1v.webp b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0ArEfmlujiPiVUD1v.webp new file mode 100644 index 0000000000..f74bc448a5 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0ArEfmlujiPiVUD1v.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0KNWOPgP4ZIQaobWo.webp b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0KNWOPgP4ZIQaobWo.webp new file mode 100644 index 0000000000..00485bf9ff Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0KNWOPgP4ZIQaobWo.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0O2FY0bB-BCao1kH2.webp b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0O2FY0bB-BCao1kH2.webp new file mode 100644 index 0000000000..26f4603e06 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0O2FY0bB-BCao1kH2.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0RfJiqYXXSujb8WCs.webp b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0RfJiqYXXSujb8WCs.webp new file mode 100644 index 0000000000..6b7afe3888 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0RfJiqYXXSujb8WCs.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0bzkdx2Ls7IbhP1NZ.webp b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0bzkdx2Ls7IbhP1NZ.webp new file mode 100644 index 0000000000..bc71ca23ce Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0bzkdx2Ls7IbhP1NZ.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0cmfHUry8I1ztX80M.webp b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0cmfHUry8I1ztX80M.webp new file mode 100644 index 0000000000..db3286591a Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0cmfHUry8I1ztX80M.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0lz9RcWYLigKtCaFp.gif b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0lz9RcWYLigKtCaFp.gif new file mode 100644 index 0000000000..301ee9949b Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0lz9RcWYLigKtCaFp.gif differ diff --git a/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0mCfvSsh_GZmW2xYn.webp b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0mCfvSsh_GZmW2xYn.webp new file mode 100644 index 0000000000..06665822e5 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0mCfvSsh_GZmW2xYn.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0qQ342c4cR9qRce74.gif b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0qQ342c4cR9qRce74.gif new file mode 100644 index 0000000000..007a6baf6a Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/0qQ342c4cR9qRce74.gif differ diff --git a/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/1cEO7t2m1M3nH9RWx0CJtaQ.webp b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/1cEO7t2m1M3nH9RWx0CJtaQ.webp new file mode 100644 index 0000000000..a1da386025 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/1cEO7t2m1M3nH9RWx0CJtaQ.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/1sW6TueDcNzw806cjigLJaA.webp b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/1sW6TueDcNzw806cjigLJaA.webp new file mode 100644 index 0000000000..52d4c4babc Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/1sW6TueDcNzw806cjigLJaA.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/1seN7hLXrHJDEN3lH-IBZmw.webp b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/1seN7hLXrHJDEN3lH-IBZmw.webp new file mode 100644 index 0000000000..2fcef31418 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/1seN7hLXrHJDEN3lH-IBZmw.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/1vJh-yghl3kJJdW0ZakbSzg.webp b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/1vJh-yghl3kJJdW0ZakbSzg.webp new file mode 100644 index 0000000000..266494aa0d Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/images/1vJh-yghl3kJJdW0ZakbSzg.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-12-what-a-year/index.md b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/index.md new file mode 100644 index 0000000000..ea5c858600 --- /dev/null +++ b/sites/www/content/blog/announcing-flutter-1-12-what-a-year/index.md @@ -0,0 +1,232 @@ +--- +title: "Announcing Flutter 1.12: What a year!" +description: "Posted by Chris Sells, Product Manager, Flutter developer experience" +publishDate: 2019-12-11 +author: csells +image: images/1sW6TueDcNzw806cjigLJaA.webp +category: releases +layout: blog +--- + +Today we’re pleased to announce version 1.12, the latest stable Flutter release. This makes 5 stable releases since our 1.0 release in December, 2018. It’s been an amazing year! We’ve closed 5,303 issues and merged 5,950 pull requests from 484 contributors. In the Flutter engine and framework, we’ve added support for Android App Bundles, iOS 13, implemented mouse and keyboard events, released the In-App Purchase plugin, merged [several](https://github.com/flutter/engine/pull/12385) [important](https://github.com/flutter/flutter/pull/36482) [performance](https://github.com/flutter/engine/pull/10182) [improvements](https://github.com/flutter/flutter/pull/37275), localized for 24 additional locales and created several new widgets. Furthermore, the Flutter tools have seen a great deal of improvement as well, with the release of Dart DevTools, which provides the widget inspector, memory and CPU profiling, and enhanced logging that can be used regardless of your editor/IDE of choice. Also, we’ve added auto-import of packages for referenced types, explicit ChromeOS support, UI Guides to make your build methods easier to read and write, and improved error messages with formatting, colors and more actionable wording. + +And with each release, we’ve said the same thing — that we’re just getting started. This continues to be true in this, our biggest release yet, coming from 188 contributors, closing 4,571 issues and merging 1,905 PRs. As in previous releases, enhancements abound for both the Flutter framework, and the tooling. + +## Flutter framework + +This release includes a visual refresh to support iOS 13 that includes completed implementation of Dark Mode, new Cupertino widgets, several UX tweaks, and a greatly enhanced Add-to-App experience. + +## iOS 13 dark mode completed + +More big news in Flutter 1.12 is the completion of our work to support the iOS 13 look and feel. This includes complete dark mode support in the Cupertino widgets. + +<DashImage figure src="images/1sW6TueDcNzw806cjigLJaA.webp" /> + + +If you’ll notice in the screen shots above, dark mode support is much more than just swapping out the background color, but also adapting the rest of the colors to be a good match. Such deep attention to dark mode was a huge amount of work, but worth it to get pixel-perfect iOS design support across both dark and light mode. + +Also, in our continuing goal for pixel-perfection for iOS 13, we’ve added two new widgets. + +<DashImage figure src="images/1seN7hLXrHJDEN3lH-IBZmw.webp" alt="[CupertinoContextMenu](https://api.flutter.dev/flutter/cupertino/CupertinoContextMenu-class.html) and [CupertinoSlidingSegmentedControl](https://api.flutter.dev/flutter/cupertino/CupertinoSlidingSegmentedControl-class.html)" caption="[CupertinoContextMenu](https://api.flutter.dev/flutter/cupertino/CupertinoContextMenu-class.html) and [CupertinoSlidingSegmentedControl](https://api.flutter.dev/flutter/cupertino/CupertinoSlidingSegmentedControl-class.html)" /> + + +And finally, in our quest to make a Flutter app feel as well as look native on iOS 13, we’ve [improved scrollbar fidelity](https://github.com/flutter/flutter/pull/41799), [provided for adaptive CupertinoAlertDialog padding](https://github.com/flutter/flutter/pull/42967), and [allowed for min/max date constraints on the CupertinoDatePicker](https://github.com/flutter/flutter/pull/44149). + +## Add-to-App updated + +Another improvement in our mobile support is an update to Add-to-App, which is the ability to integrate Flutter into an existing Android or iOS app. Here, we’ve been working on simplifying the integration flow to make adding a Flutter library to your app a better experience, including the addition of a new Flutter Module wizard in Android Studio. + +<DashImage figure src="images/0bzkdx2Ls7IbhP1NZ.webp" /> + + +With Flutter 1.12, Add-to-App is now officially supported for adding one fullscreen Flutter instance to your app. In supporting this functionality, we’ve also: + +* Stabilized the APIs for platform integration in Java, Kotlin, Objective-C and Swift, including a new set of APIs for Android. See [the Android project migration docs](http://flutter.dev/go/android-project-migration) for details on changes. + +* Added support for using plugins in embedded Flutter modules. + +* Provided additional integration mechanisms via [Android AARs](https://flutter.dev/docs/development/add-to-app/android/project-setup#option-a---depend-on-the-android-archive-aar) and [iOS Frameworks](https://flutter.dev/docs/development/add-to-app/ios/project-setup#option-b---embed-frameworks-in-xcode) for better compatibility with existing build systems. + +* Reworked the ‘flutter attach’ mechanism on the command-line tools, VSCode and IntelliJ plugins to easily attach onto a running Flutter module for debugging, DevTools and hot reload. + +To try Add-to-App, see the [website documentation](https://flutter.dev/docs/development/add-to-app) or browse our [sample projects](https://github.com/flutter/samples/tree/master/experimental/add_to_app) demonstrating various integration scenarios. + +## Dart 2.7 + +Of course, everything we do in Flutter is based on Dart, so if you haven’t already read about extension methods and safe string handling (including emojis), or want an update on null safety using non-nullable types, you can find that information in the [Dart 2.7 announcement](https://medium.com/dartlang/dart-2-7-a3710ec54e97). + +## Beyond Flutter 1.12 stable + +At the same time that Flutter is getting new features in the stable channel, it’s also getting new features beyond what you can do in stable, specifically a beta release of web support and an alpha release of macOS support. + +## Web support available in beta + +The Flutter 1.12 master, dev and beta channels all provide improved support for web. + +<DashImage figure src="images/0KNWOPgP4ZIQaobWo.webp" /> + + +One happy customer of Flutter on the web is [Rivet](https://rivet.area120.com/link/flutter), pictured above. Rivet is an education project that currently has a mobile app in production. They’re using Flutter and Firebase to build a web version of their app that they plan to launch in early 2020. + +You can learn more about what other customers are doing with Flutter’s web support as well as the rest of the details in the [Flutter web blog post](https://medium.com/flutter/web-support-for-flutter-goes-beta-35b64a1217c0). + +## macOS moving to alpha + +macOS desktop support isn’t far behind, moving from tech preview to alpha, available now in both master and dev channels (in Flutter SDK 1.13). + +<DashImage figure src="images/02cmIipg-JivBAlRW.webp" /> + + +Pictured here at desktop size is [a new version of the Flutter Gallery](https://flutter.github.io/samples/#/) that’s been completely updated to support macOS in addition to Android, iOS, and web. + +The macOS alpha represents a big step forward for Flutter’s desktop support, including the new DataTree and Split sample widgets, several plugins ported to macOS, support for building in both release and profiling mode, and a greatly simplified tooling story. If you’re running from the dev or master channel, you can gain access to the macOS tooling by enabling macOS desktop support in Flutter’s system-wide config: + +`$ flutter config --enable-macos-desktop` + +Creating a Flutter project that runs on macOS is now just like creating any other new Flutter project with ‘flutter create’. + +<DashImage figure src="images/00IEDbMjBcAYcEayr.webp" alt="Notice the new macos directory created by default" caption="Notice the new macos directory created by default" /> + + +In addition to the tooling support, we’ve also been working on the density that’s appropriate for desktop-sized apps. Mobile apps need relatively large controls to accommodate touch interactions whereas on desktop-sized devices, a user is more likely to be using a mouse. In bringing Flutter to the desktop, we’ve worked on allowing you to choose the density of your widgets to better accommodate the needs of your desktop users: + +<DashImage figure src="images/0lz9RcWYLigKtCaFp.gif" alt="[Sample](https://github.com/gspencergoog/density_sample) demonstrating Flutter’s implementation of [the Material Density guidelines](https://material.io/design/layout/applying-density.html)" caption="[Sample](https://github.com/gspencergoog/density_sample) demonstrating Flutter’s implementation of [the Material Density guidelines](https://material.io/design/layout/applying-density.html)" /> + + +And finally, to improve the experience of Flutter desktop apps, we’ve done a lot of work on keyboard navigation and keyboard access, including: + +* [synchronizing modifier keys with key events](https://github.com/flutter/flutter/pull/43948) + +* [managing item selection when a dropdown is opened](https://github.com/flutter/flutter/pull/43722) + +* [adding a convenience accessor for primary focus](https://github.com/flutter/flutter/pull/43859) + +* [adding keyboard navigation, hover and shortcuts for switches](https://github.com/flutter/flutter/pull/43384) + +* [checkboxes and radio buttons](https://github.com/flutter/flutter/pull/43384) + +* [automatic scrolling to keep focused items in view](https://github.com/flutter/flutter/pull/44965) + +* [keyboard shortcut-based scrolling](https://github.com/flutter/flutter/pull/45019) + +* [a new widget for handling focus and hover](https://github.com/flutter/flutter/pull/44867) + +* [rewritten copy/paste and keyboard selection](https://github.com/flutter/flutter/pull/44130) + +* [keyboard navigation of dropdowns](https://github.com/flutter/flutter/pull/42811) + +* [visual density support](https://github.com/flutter/flutter/pull/43547) + +* [adding macOS function key support](https://github.com/flutter/flutter/pull/44410) + +In addition to the Flutter sample, we also recommend [the new Photos Search sample](https://github.com/flutter/samples/tree/master/experimental/desktop_photo_search), which shows off a lot of desktop goodness, including keyboard handling, the new widget density, the new plugins, and the new widgets. + +For those of you curious about progress on Windows and Linux, they’re still in technical preview, but both benefit from a lot of the work to get macOS to alpha. We’ll share the updates to those platforms soon. For more details of where we are with desktop support in Flutter for macOS, Windows and Linux, please see [flutter.dev/desktop](http://flutter.dev/desktop). + +## Flutter tooling + +In addition to the Framework and Engine, we also have a lot to talk about for Flutter tooling. This includes a new version of DartPad with support for Flutter, augmented IntelliJ-based IDEs with a preview of a new feature we’re calling “Hot UI”, enhanced Dart DevTools with a new visual layout view, enabled simultaneous multi-device debugging in Visual Studio Code, improved the Android build process and better support for finding differences in rendered widgets between test runs. + +## DartPad loves Flutter + +If you aren’t already using [DartPad](https://dartpad.dev), you should try it out! It’s a great way to try Dart features without installing anything. Furthermore, with the new release of DartPad, now you get Flutter, too! + +<DashImage figure src="images/0O2FY0bB-BCao1kH2.webp" /> + + +The new DartPad leverages Flutter’s web support so that when you’re writing Flutter code on the left, you’re running a real, live Flutter (web) program on the right. The chief difference is that using DartPad, you can get started with Flutter without installing a thing. + +In addition to the stand-alone DartPad playground, we’ve also started adding DartPad with Flutter support into our docs and in our codelabs (like [Basic Flutter layout concepts](https://flutter.dev/docs/codelabs/layout-basics) and [Implicit animations](https://flutter.dev/docs/codelabs/implicit-animations)), so that you can learn about Flutter from the comfort of your browser. For more information on DartPad, please check out our [DartPad announcement post](https://medium.com/dartlang/a-brand-new-dartpad-dev-with-flutter-support-16fe6027784). + +## Build your widgets inline with Hot UI + +If you install the Flutter tools locally on your own machine (and we hope you will), you’ll find a new feature previewed in the IntelliJ/Android Studio plugin for Flutter. It allows you to see and interact with your widgets directly in your IDE as you’re building them. + +We call this feature “Hot UI” and, like Hot Reload, as you make the changes in your code, it updates the hosted UI directly. You can also interact with the hosted UI (like changing a color, as shown here) and that change goes directly into your code. To enable the Hot UI preview, you can [read the instructions on the Flutter wiki](https://github.com/flutter/flutter-intellij/wiki/HotUI-Getting-Started-instructions). + +## Debug layout issues with the Layout Explorer + +Whether you write the code by hand or let Hot UI write it for you, you’ve still got code and sometimes code has issues. Helping you track down and fix your issues is exactly why Dart DevTools was invented. In this new version of DevTools, we’ve added a feature called the “Layout Explorer” to augment the Inspector with a visual representation of your layout. + +<DashImage figure src="images/0qQ342c4cR9qRce74.gif" /> + + +Not only does the Layout Explorer help you to visualize the layout of the widgets in your running app, but if you’d like to experiment with changing the layout options, it allows you to do so interactively. We’re hoping the preview of this feature helps make it easier to understand and fix your layout issues. To enable this feature, see [the Layout Explorer docs](https://flutter.dev/docs/development/tools/devtools/inspector#flutter-layout-explorer). + +## Multi-device debugging + +When you’ve built and debugged your Flutter UI, you have most likely done it on a single device. Wouldn’t it be nice to be able to debug your app across multiple devices (physical or virtual) at the same time? With Flutter’s support for multi-session debugging in Visual Code, that’s just what you can do. + +<DashImage figure src="images/1cEO7t2m1M3nH9RWx0CJtaQ.webp" /> + + +Here we’ve got the same Flutter app running simultaneously on three separate debugging sessions. If we make a change in the code, Hot Reload makes sure that it’s reflected in all three apps. If we set a breakpoint, whichever app triggers that code gets stopped. If you’d like to stop debugging one, you can do so without stopping them all. You can learn how to configure this feature for [multiple device debugging on the wiki](https://github.com/flutter/flutter/wiki/Multi-device-debugging-in-VS-Code). + +## Android build improvements + +And finally, to continue to improve Android, we addressed some build problems in this release. Firstly, we made the Android build more robust, specifically around combining plugins using Support Libraries and those using AndroidX. We did this by moving the Flutter team’s plugins to AndroidX and [we recommend that apps and plugins move to AndroidX as well](https://flutter.dev/docs/development/packages-and-plugins/plugin-api-migration). However, for plugins that haven’t yet moved, if there is a build problem, we have an alternate code path in our build that uses Android Archive files and Jetifier. The build is slower, which is why it’s not the primary build mechanism, but we find that it solves about 95% of the build problems we’ve encountered. + +Another issue we addressed was deprecating Proguard in favor of [R8](https://developer.android.com/studio/build/shrink-code), the new code optimizer from Google. Before this release, the app author had to configure ProGuard rules manually using guidance provided by the plugin author. In this release, plugins can define their rules in the source code and R8 consumes these rules automatically, saving the app developer that headache. + +Furthermore, in our attempt to continue to make Flutter as slim as possible, we reduced the Hello, World app size for Android by 2.6% (reducing it from 3.8MB to 3.7MB). Every little bit helps! + +## Golden image testing + +The term “golden image” refers to a master image file that is considered the true rendering of a given widget, state, application, or other visual representation you have chosen to capture. In Flutter 1.12, we have implementations of the [GoldenFileComparator](https://api.flutter.dev/flutter/flutter_test/GoldenFileComparator-class.html) and [LocalFileComparator](https://api.flutter.dev/flutter/flutter_test/LocalFileComparator-class.html) classes that compare by pixels instead of bits, [eliminating false positives](https://github.com/flutter/flutter/issues/30036). These new implementations highlight visual differences to make it clear when there are differences between your golden image and the updates under testing. + +<DashImage figure src="images/0RfJiqYXXSujb8WCs.webp" /> + + +In this case, it’s clear that the differences between the master and the test image are all in the border, making it much easier to track down the discrepancy. + +## Community + +In addition to all of the work on Flutter and its associated tooling, the Flutter community continues to take Flutter into new and amazing directions! To see what developers in the community are doing, we’ve put together a little video. + +<YoutubeEmbed id="tWCr9vlGz_8" title="Developers using Flutter around the world" fullwidth="true"/> + + +We’re so lucky to have such a great set of developers in the Flutter community. You make us all proud to be on the Flutter team! + +## Flutter Favorite packages + +When we released Flutter 1.0 in December, 2018, there were about 1000 packages on pub.dev that supported Flutter and we thought that was a huge number. As of this writing, that number has increased by more than 6x. With that many options, it’s sometimes hard to know which packages to choose. The overall score on pub.dev helps as well as the new [Verified Publishers](https://medium.com/dartlang/verified-publishers-98f05466558a) feature. Now, pub.dev is getting [a rating system](http://go/dart-2.7-annoucement), which should help even more. + +Still, our users have asked again and again for a set of “recommended” packages and plugins. With that in mind, we’re pleased to announce [the Flutter Favorite program](https://flutter.dev/docs/development/packages-and-plugins/favorites). + +<DashImage figure src="images/08Q2gB989P4yclIqD.webp" /> + + +A Flutter Favorite package (or plugin) is one that we think that you should consider first when building your app. The “we” in that sentence is the Flutter Ecosystem Committee, a group of regionally-diverse individuals picked from the Flutter team at Google and from the Flutter community at large to address issues across the Flutter ecosystem. Their first job was to establish a high quality bar and to identify an initial set of packages that met that quality bar. The authors of those packages are able to use the Flutter Favorite logo in their package documentation. Furthermore, pub.dev has been updated to show the logo in search results and other places. + +For details, see [the Flutter Favorite page on flutter.dev](https://flutter.dev/docs/development/packages-and-plugins/favorites). You can also see [the complete list of Flutter Favorite packages on pub.dev](https://pub.dev/flutter/favorites). The bottom line is that if you’re an app developer and you see that logo, you should have confidence in that package. If you’re a package author and you’ve been awarded the Flutter Favorite logo, thank you for your contribution to the Flutter ecosystem. + +## Community tools + +And speaking of contributions to be proud of, the Flutter community at large has been building a number of excellent tools as well; here’s just a selection. + +<DashImage figure src="images/0mCfvSsh_GZmW2xYn.webp" alt="[Flutter Device Preview](https://github.com/aloisdeniel/flutter_device_preview)" caption="[Flutter Device Preview](https://github.com/aloisdeniel/flutter_device_preview)" /> + + +<DashImage figure src="images/0ArEfmlujiPiVUD1v.webp" alt="[Widget Maker](https://www.widgetmaker.dev/)" caption="[Widget Maker](https://www.widgetmaker.dev/)" /> + + +<DashImage figure src="images/1vJh-yghl3kJJdW0ZakbSzg.webp" alt="[Panache](https://rxlabz.github.io/panache/#/)" caption="[Panache](https://rxlabz.github.io/panache/#/)" /> + + +## Featured tooling partner: Nevercode + +In addition to a great set of community tools, the Flutter ecosystem has a great set of tooling partners as well. One notable partner has always been Nevercode, who have a whole new set of features in their latest release, including [a Visual Studio Code plugin called Remote Mac](https://marketplace.visualstudio.com/items?itemName=codemagic.remote-mac). + +<DashImage figure src="images/0cmfHUry8I1ztX80M.webp" /> + + +The Remote Mac extension lets you connect directly to a Mac that they’ve hosted in the cloud for purposes of testing your iOS and macOS Flutter code. For more details about Nevercode’s latest release, including new support for Flutter on the web and on macOS, new enterprise features and more, check out [their newest blog post](https://blog.codemagic.io/more-professional-capable-accessible/). + +To see the progress being made by our other tooling partners, you should definitely [check out what Supernova, Rive and Adobe are up to in their latest releases](https://developers.googleblog.com/2019/12/flutter-ui-ambient-computing.html). + +## Conclusion + +This has definitely been a big year for Flutter and v1.12 is a big release. This blog post has been a whirlwind tour of what’s new in this release; if you’d like to check on your favorite pull release, see where we’ve been spending our time in this release by how many pull releases in each area or see what we broke, then we recommend [the Flutter 1.12 Release Notes](https://flutter.dev/docs/development/tools/sdk/release-notes/release-notes-1.12.13). + +We hope you agree that Flutter is moving in the right direction and picking up speed. With all of these new features and new tools, where do you want your app to run today? \ No newline at end of file diff --git a/sites/www/content/blog/announcing-flutter-1-17/images/0RMNYoiBJv-U3OC5C.gif b/sites/www/content/blog/announcing-flutter-1-17/images/0RMNYoiBJv-U3OC5C.gif new file mode 100644 index 0000000000..9f99c9e072 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-17/images/0RMNYoiBJv-U3OC5C.gif differ diff --git a/sites/www/content/blog/announcing-flutter-1-17/images/0YPmVAVbn2SqtHpRF.webp b/sites/www/content/blog/announcing-flutter-1-17/images/0YPmVAVbn2SqtHpRF.webp new file mode 100644 index 0000000000..48f9165766 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-17/images/0YPmVAVbn2SqtHpRF.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-17/images/0_Vc08nu8x7JFksB0.gif b/sites/www/content/blog/announcing-flutter-1-17/images/0_Vc08nu8x7JFksB0.gif new file mode 100644 index 0000000000..0d822f925b Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-17/images/0_Vc08nu8x7JFksB0.gif differ diff --git a/sites/www/content/blog/announcing-flutter-1-17/images/0kfSA8iTjVjrsPIyG.webp b/sites/www/content/blog/announcing-flutter-1-17/images/0kfSA8iTjVjrsPIyG.webp new file mode 100644 index 0000000000..57b528bed0 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-17/images/0kfSA8iTjVjrsPIyG.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-17/images/0nT-kBS1YB0XqZE4C.webp b/sites/www/content/blog/announcing-flutter-1-17/images/0nT-kBS1YB0XqZE4C.webp new file mode 100644 index 0000000000..0bb5fd0a79 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-17/images/0nT-kBS1YB0XqZE4C.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-17/images/0qB_uRnhlu9bb2C5i.webp b/sites/www/content/blog/announcing-flutter-1-17/images/0qB_uRnhlu9bb2C5i.webp new file mode 100644 index 0000000000..9b326d6f75 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-17/images/0qB_uRnhlu9bb2C5i.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-17/images/0vaELGcqyjmgMMF7W.webp b/sites/www/content/blog/announcing-flutter-1-17/images/0vaELGcqyjmgMMF7W.webp new file mode 100644 index 0000000000..18bb1d71c1 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-17/images/0vaELGcqyjmgMMF7W.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-17/images/0vjV5MYcLgBJmj_Ix.webp b/sites/www/content/blog/announcing-flutter-1-17/images/0vjV5MYcLgBJmj_Ix.webp new file mode 100644 index 0000000000..156c90217a Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-17/images/0vjV5MYcLgBJmj_Ix.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-17/images/0vwCZv9MQd_BLv7ii.webp b/sites/www/content/blog/announcing-flutter-1-17/images/0vwCZv9MQd_BLv7ii.webp new file mode 100644 index 0000000000..0ec582e5dd Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-17/images/0vwCZv9MQd_BLv7ii.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-17/images/14MIF2GwByNG1MJ7DNslb3Q.webp b/sites/www/content/blog/announcing-flutter-1-17/images/14MIF2GwByNG1MJ7DNslb3Q.webp new file mode 100644 index 0000000000..1596ac3903 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-17/images/14MIF2GwByNG1MJ7DNslb3Q.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-17/images/1BitMBFadDAEiHFN5Al1mjQ.webp b/sites/www/content/blog/announcing-flutter-1-17/images/1BitMBFadDAEiHFN5Al1mjQ.webp new file mode 100644 index 0000000000..da2a9a0c74 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-17/images/1BitMBFadDAEiHFN5Al1mjQ.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-17/images/1Iy0WlfOaCAvgFwWwe4SKwQ.webp b/sites/www/content/blog/announcing-flutter-1-17/images/1Iy0WlfOaCAvgFwWwe4SKwQ.webp new file mode 100644 index 0000000000..5800a3d0ba Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-17/images/1Iy0WlfOaCAvgFwWwe4SKwQ.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-17/images/1Jlt_w6Bs7KAae42rYkFlwg.webp b/sites/www/content/blog/announcing-flutter-1-17/images/1Jlt_w6Bs7KAae42rYkFlwg.webp new file mode 100644 index 0000000000..38159965cf Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-17/images/1Jlt_w6Bs7KAae42rYkFlwg.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-17/images/1OU1YDFMKeSUiaavoWohEUA.webp b/sites/www/content/blog/announcing-flutter-1-17/images/1OU1YDFMKeSUiaavoWohEUA.webp new file mode 100644 index 0000000000..499a73949e Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-17/images/1OU1YDFMKeSUiaavoWohEUA.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-17/index.md b/sites/www/content/blog/announcing-flutter-1-17/index.md new file mode 100644 index 0000000000..81f9b812ea --- /dev/null +++ b/sites/www/content/blog/announcing-flutter-1-17/index.md @@ -0,0 +1,187 @@ +--- +title: "Announcing Flutter 1.17" +description: "Includes Metal support for faster iOS performance, new Material components, new Network tracking tooling and more!" +publishDate: 2020-05-06 +author: csells +image: images/14MIF2GwByNG1MJ7DNslb3Q.webp +category: releases +layout: blog +--- + +*Posted by Chris Sells, Product Manager, Flutter developer experience* + +Today we’re pleased to announce Flutter 1.17, our first stable release of 2020. + +This year has been a challenging one for everyone. Our goal is to ship stable releases on a roughly quarterly cadence; however, this release took a little longer because we’ve been [retooling our infrastructure for a new release process](https://medium.com/p/f723d898d7af#d6e5). We continue to make quality our #1 priority and believe that the new release model will improve our ability to service the stable branch with quality hotfixes. This release is full of fixes, with an unprecedented 6,339 issues closed since our 1.12 stable release. A great deal of this increase is due to [our partnership with Nevercode](https://blog.codemagic.io/flutter-and-codemagic-join-forces-on-github/), which has given us increased responsiveness when it comes to responding to customer issues. So far this year, we’ve closed more bugs than have been opened, resulting in a net decrease of ~800 issues. Many of these bugs were fixed by the 3,164 PRs we merged from 231 contributors. These are huge numbers and our heartfelt thanks goes out to everyone for their hard work and continued contributions during this challenging time. + +If you’re interested in the full list of PRs we’ve merged in this release, you can see that on [the flutter.dev site](https://flutter.dev/docs/development/tools/sdk/release-notes/changelogs/changelog-1.17.0). In addition to the quality improvements, we’ve managed to sneak some new features into this release as well, including Metal support on iOS, new Material components, new Network tracking tooling and more! This release also includes Dart 2.8, about which you can [read more on the Dart blog](https://medium.com/dartlang/announcing-dart-2-8-7750918db0a). + +## Mobile performance and size improvements + +A major focus of this release has been continued overall work on performance and memory improvements. Just by upgrading your app to this release, your users should see faster animations, smaller apps and lower memory utilization. In this release, you’ll see a [20%-37% speedup](https://github.com/flutter/flutter/pull/48900) for the default navigation case (opaque routes with no transparency). You’ll also see up to a 40% reduction in CPU/GPU usage for simple iOS animations, depending on the hardware (detailed in [PR 14104](https://github.com/flutter/engine/pull/14104) and in [PR 13976](https://github.com/flutter/engine/pull/13976)). + +This release also provides a considerable app size improvement due to [several](https://github.com/dart-lang/sdk/commit/f56b0f690789b6f0e2e5bc1340abf4eba414b7a0) [fixes](https://github.com/dart-lang/sdk/commit/a2bb7301c5795e6b28089a8dc96e6ab5ca798e22) [that](https://github.com/dart-lang/sdk/commit/d77f4913a18ecce8c4be95cbaa4299ff1521dc10) [add](https://github.com/dart-lang/sdk/commit/e2faac751e1ef3707730e6e48f4d8f22ecbf35c3) [up](https://github.com/dart-lang/sdk/commit/8e7ffafbafc8203361111ddcafe0e0fcc372edf8) to a big result. For example, the Flutter Gallery sample for Android was 9.6MB at the end of 2019 and is now 8.1MB, which is an 18.5% reduction. + +For memory usage, this release brings [a 70% memory reduction](https://github.com/flutter/engine/pull/14265) in fast scrolling through large images, which can also result in a performance increase depending on how much memory the device has. + +<DashImage figure src="images/0vwCZv9MQd_BLv7ii.webp" alt="*Test app memory usage for PRs merged over time (shorter bars are better)*" caption="*Test app memory usage for PRs merged over time (shorter bars are better)*" /> + + +However the broadest performance improvement is the support for Metal in iOS. + +## Metal support improves performance by 50% on iOS + +Apple’s support for Metal on iOS provides nearly direct access to the underlying GPU and is Apple’s recommended graphics API. On the iOS devices that fully support Metal, Flutter now uses it by default, making your Flutter apps run faster most of the time, increasing [rendering speeds by about 50% on average](https://github.com/flutter/flutter/issues/53768) (depending on your workload). + +<DashImage figure src="images/0YPmVAVbn2SqtHpRF.webp" alt="*Test iOS app frame rendering time for OpenGL vs. Metal (shorter bars are better)*" caption="*Test iOS app frame rendering time for OpenGL vs. Metal (shorter bars are better)*" /> + + +For the devices that don’t support Metal fully (those prior to the A7 processor or those running iOS versions prior to 10), Flutter uses OpenGL as it has in the past, providing native rendering speeds for older devices. For more details, check out [the Metal on iOS FAQ on the Flutter wiki](https://github.com/flutter/flutter/wiki/Metal-on-iOS-FAQ). + +## Material widgets: NavigationRail, DatePicker and more + +We continue to evolve and improve the Material design system implemented in Flutter, based on feedback from internal and external customers. In this release, we’ve added [`NavigationRail`](https://master-api.flutter.dev/flutter/material/NavigationRail-class.html), a new widget that offers a responsive app navigation model. It was [designed](https://material.io/components/navigation-rail) and implemented by the Google Material Design team. The `NavigationRail` is great for apps that can switch between mobile and desktop form factors because it’s very easy to swap in for a `BottomNavigator` as your app’s screen size increases. + +<DashImage figure src="images/0qB_uRnhlu9bb2C5i.webp" alt="The new NavigationRail widget" caption="The new NavigationRail widget" /> + + +To see the `NavigationRail` in action, check out [the web_dashboard sample](https://github.com/flutter/samples/tree/master/experimental/web_dashboard) or try it [on DartPad](https://dartpad.dev/b9c6cd345fd1cff643353c1f4902f888). + +In addition to the new widget, this release includes updates to the Material [`DatePicker`](https://api.flutter.dev/flutter/material/showDatePicker.html) and text selection overflow widgets. + +<DashImage figure src="images/1Iy0WlfOaCAvgFwWwe4SKwQ.webp" alt="Updated Material DatePicker widget" caption="Updated Material DatePicker widget" /> + + +This `DatePicker` release includes new visuals to match the [updated Material guidelines](https://material.io/components/pickers/#mobile-pickers) as well as a new text input mode. You can read about the details in the [Material Date Picker Redesign](https://flutter.dev/go/material-date-picker-redesign) specification. + +<DashImage figure src="images/0RMNYoiBJv-U3OC5C.gif" alt="Updated text selection overflow on Android" caption="Updated text selection overflow on Android" /> + + +<DashImage figure src="images/0vaELGcqyjmgMMF7W.webp" alt="Updated text selection overflow on iOS" caption="Updated text selection overflow on iOS" /> + + +The text selection menu now has improved fidelity for [Android](https://github.com/flutter/flutter/pull/49391) and [iOS](https://github.com/flutter/flutter/pull/54140) when the buttons are longer than can be displayed without overflow. This is particularly noticeable in locales where the menu item words can be longer. + +In the same timeframe as Flutter 1.17 but delivered out-of-band, we have also shipped [the new Animations Package](https://pub.dev/packages/animations) that provides pre-built animations that implement [the new Material motion specification](https://material.io/design/motion/the-motion-system.html). + +<DashImage figure src="images/14MIF2GwByNG1MJ7DNslb3Q.webp" alt="*Examples of Container transforms from the Animations package*" caption="*Examples of Container transforms from the Animations package*" /> + + +In [the Implementing Motion blog post](https://medium.com/google-design/implementing-motion-9f2839002016), the Material Design team defines four transition patterns that describe animations between components and fullscreen views: Container transform, Shared axis, Fade through, and Fade. While these animations have always been possible in Flutter, the Animations package makes it considerably easier to implement them. Drop them into your app and delight your users today! + +## Material Text Scale: Modernizing Flutter Text Theming + +In this release, the Flutter team has completed their implementation of the [Type Scale portion of the 2018 Material Design specification](https://material.io/design/typography/the-type-system.html#type-scale) while not breaking existing Flutter apps. Opt-in support for the new configurations — but not the new names — was added in [PR 22330](https://github.com/flutter/flutter/pull/22330), in October 2018. The existing text style names were not changed because doing so was a breaking API change that would likely affect most applications. This release updates the TextTheme API to match the current Material spec, but maintains the old names so that your code doesn’t break. The old names have been deprecated however, so you will get warnings to encourage you to adopt the new names. + +The names and configurations of the 2018 Material TextStyles are summarized by the following table. + +<DashImage figure src="images/1Jlt_w6Bs7KAae42rYkFlwg.webp" /> + + +Notice that the `TextStyles` called `body1` and `body2` in the Material Design specification are called `bodyText1` and `bodyText2` in the Flutter `TextTheme` API. Similarly, the `TextStyles` called `H1-H6` in the specification are called `headline1-headline6` in the `TextTheme` API. + +## Google Fonts for Flutter + +Text and fonts go hand in hand, so if you’re excited about the new Material Text Scale implementation, then you’re probably also going to be excited about [the new Google Fonts for Flutter v1.0 release](https://medium.com/flutter/introducing-google-fonts-for-flutter-v-1-0-0-c0e993617118). + +<DashImage figure src="images/0_Vc08nu8x7JFksB0.gif" alt="Google Fonts are easy to use in your Flutter apps" caption="Google Fonts are easy to use in your Flutter apps" /> + + +Google Fonts allows developers to easily experiment with and use any of the fonts from fonts.google.com in their app. When the app is ready to publish, the developer then decides whether the user receives the font by downloading it from the API, or it’s pre-bundled with the app package. + +## Accessibility and Internationalization + +Lastly, an important area of continued focus is accessibility, since we consider it a priority that Flutter apps are available to the broadest possible audience. In this release, we’ve done work across the board, including accessibility fixes for [scrolling](https://github.com/flutter/flutter/issues/43883), [text](https://github.com/flutter/flutter/issues/52487) [fields](https://github.com/flutter/flutter/issues/53065), and [other input widgets](https://github.com/flutter/flutter/issues/49259). You’ll see a full list of the accessibility issues closed in this release on [GitHub](https://github.com/flutter/flutter/issues?q=is%3Aclosed+is%3Aissue+label%3A%22a%3A+accessibility%22+closed%3A2019-11-25..2020-04-02). We encourage you to test your own apps for accessibility and have also updated our documentation in this release with [some recommended best practices](https://flutter.dev/docs/development/accessibility-and-localization/accessibility). + +On the internationalization front, we’ve been working on some issues affecting Samsung keyboard IMEs that affected text input in various East Asian languages. We’re pleased to report that we’ve completed this work, and [Korean developers in particular should find a lot to celebrate](https://github.com/flutter/flutter/issues/42273) in this release. + +## Tooling: Dart DevTools port to Flutter, Fast Start for Android and more + +This release brings us very close to swapping out the current version of Dart DevTools with the new Flutter version. If you’d like to give it a try, start DevTools and click the “beaker” icon in the upper-right corner of DevTools. + +<DashImage figure src="images/0vjV5MYcLgBJmj_Ix.webp" /> + + +While you’re using the pre-release version of Dart DevTools implemented with Flutter, you’re likely to notice all kinds of improvements, but the biggest one is the new **Network** tab. + +<DashImage figure src="images/1BitMBFadDAEiHFN5Al1mjQ.webp" /> + + +If you’re not seeing the Network tab in the prerelease version of Dart DevTools (for example, if you’re running it from the command line), you can update it manually using the following command: + +``` +$ pub global activate devtools +``` + + +The Network tab shows you network traffic for your Flutter app once you press the Record button. Further, if you’d like to catch network traffic starting immediately from when you start your app, you can include this line of code in your `main()` method: + +``` +void main() { + **// enable network traffic logging** + **HttpClient.enableTimelineLogging = true;** + runApp(MyApp()); +} +``` + + +In addition to an updated Dart DevTools, this release also adds support for [an experimental “fast start” option](https://github.com/flutter/flutter/pull/46140), which allows you to start your Flutter app debugging as much as 70% faster when targeting Android. You can access this option via `flutter run —-fast-start -d <your Android device>`. This option installs a generic Android app that only depends on your plugin code, excluding any Dart code or assets. This allows repeated `flutter run` commands to start up faster, since changes to Dart code or assets don’t require the APK to be rebuilt. This option bundles your app into a generic Android “wrapper” that doesn’t actually get installed on your device, which is different from our normal start options. Furthermore, there are some cases where it won’t work, e.g. when you’re using plugins that access background execution. On the other hand, if your Android debugging startup time is getting you down, give it a try. + +Another change to note if you’re targeting Android is that now AndroidX is the only option when creating a new Flutter project. [AndroidX](https://developer.android.com/jetpack/androidx) libraries provide advanced Android functionality known as [Android Jetpack](https://developer.android.com/jetpack/?gclid=Cj0KCQjwka_1BRCPARIsAMlUmEpxmZqWZyO2NTx1F_aYYRm0EUtwl6Rlr2ViKXwZpsQKo7ailItPdJkaAhnNEALw_wcB). In the last release, we deprecated the original Android Support Library and moved to AndroidX as the default for all new projects. With this release, the --`androidx` flag to `flutter create` is now the only option available. While existing applications that don’t use AndroidX can continue to be compiled with Flutter, [now is a great time](https://flutter.dev/docs/development/androidx-migration) to migrate over to the new library. + +If you’re an Android Studio or IntelliJ user, you’ll find that the Hot Reload feature is more resilient. Prior to this change, if you had any analysis errors, Hot Reload wouldn’t reload your code. This can be pretty frustrating if the analysis error is something that didn’t affect the code that you’re currently running, like in unit tests. With [this change](https://groups.google.com/forum/m/#!topic/flutter-announce/tTgQcTgqrKg), analysis errors aren’t enough to keep Hot Reload from working, depending instead on compiler errors from the VM. + +If you’d like earlier access to changes like these in the Flutter plugin for Android Studio or IntelliJ, we now have a dev channel for the IntelliJ plugin that you may opt into for a faster cadence of updates. The goal with the Dev Channel is to gather feedback from Flutter developers on new IDE integration features before we make them more broadly available. If you’re adventurous and would like to provide early feedback to the Flutter tooling team, please [sign up today](https://groups.google.com/forum/m/#!topic/flutter-announce/tTgQcTgqrKg)! + +For Visual Studio Code users, we recommend the new **Dart: List Outdated Packages** command that runs [the new ‘pub outdated’ command](https://medium.com/dartlang/announcing-dart-2-8-7750918db0a). + +<DashImage figure src="images/0kfSA8iTjVjrsPIyG.webp" /> + + +This command is meant to help you track down versioning issues in your dependencies. + +Last but not least, if you find yourself with a Flutter crash, you’ll be prompted to file the bug by the tools. + +<DashImage figure src="images/1OU1YDFMKeSUiaavoWohEUA.webp" /> + + +These bug reports are closely watched by the team for severity and frequency, so please do log them when prompted. + +## Customer Shoutout: MGM and Superformula + +Of course, Flutter exists to enable beautiful application experiences. One example of a digital agency doing amazing things is [Superformula](https://superformula.com/flutter/), who has recently worked with MGM Resorts to deliver [a major update to their mobile application](https://www2.mgmresorts.com/app/), having completely rebuilt it in Flutter. They report that “adding Flutter to our core offerings has unlocked added speed and flexibility, which translates to real, measurable value for both our clients and their users.” + +<DashImage figure src="images/0nT-kBS1YB0XqZE4C.webp" /> + + +Superformula worked with the MGM Resorts design team to create a new MGM design language for all major web, mobile, and kiosk experiences. With a smaller team and all-new Flutter codebase, they were able to get the app rebuilt and shipped to both app stores much faster than they were able to before, resulting in a 9% increase in booking conversions for MGM. + +## Breaking Changes + +As always, we try to minimize the number of breaking changes with each new release of Flutter while still balancing our ability to ensure that Flutter provides an intuitive, flexible API that can support new idioms on new platforms. In [a user survey last year](https://medium.com/p/3659b02303a5#4bad), you told us that you were tolerant of carefully-considered breaking changes that improve the framework. So we’re continuing to gently evolve the API. These are the breaking changes in this release. + +* [#42100](https://github.com/flutter/flutter/pull/42100) [Run secondary animation of previous route when using pushReplacement(…](https://groups.google.com/g/flutter-announce/c/y0SvesRHlcE/m/39TuR5FVDQAJ) + +* [#45940](https://github.com/flutter/flutter/pull/45940) Deprecate UpdateLiveRegionEvent + +* [#49389](https://github.com/flutter/flutter/pull/49389) Defer image decoding when scrolling fast + +* [#49391](https://github.com/flutter/flutter/pull/49391) Text Selection Overflow (Android) + +* [#49771](https://github.com/flutter/flutter/pull/49771) [Assert cache hints are not set for null painters](https://groups.google.com/g/flutter-announce/c/gDfazJIBdDo/m/d5AC8gR3FQAJ) + +* [#50318](https://github.com/flutter/flutter/pull/50318) [Live image cache](https://groups.google.com/g/flutter-announce/c/IdfjYvRBR4c/m/1_JxffXTGAAJ) + +* [#50354](https://github.com/flutter/flutter/pull/50354) [Use strut box heights to calculate selection rectangles in order to ensure that they remain within visible bounds](https://groups.google.com/g/flutter-announce/c/hVP699NQ7PQ/m/BgVgmsAdFwAJ) + +* [#50733](https://github.com/flutter/flutter/pull/50733) Generate message lookup in gen_l10n + +* [#51435](https://github.com/flutter/flutter/pull/51435) remove isinitialroute from RouteSettings + +* [#52781](https://github.com/flutter/flutter/pull/52781) Move mouse_tracking.dart to rendering + +* [#44930](https://github.com/flutter/flutter/pull/44930) [Navigator 2.0: Refactor the imperative api to continue working in the new navigation system](https://groups.google.com/g/flutter-announce/c/DSAeObWGpoY) + +## Conclusion + +As our mobile support continues to mature while we bring the [web closer to production quality](https://medium.com/flutter/flutter-web-support-updates-8b14bfe6a908), Flutter brings with it the promise of solving a problem that our industry has wrestled with for decades: how can you build great apps from a single source code base across multiple platforms? With all of the power and capability that Flutter provides, we think we’re on a good path to answer that question. What are you going to build? \ No newline at end of file diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/00Xhr32bM0mcyJFXl.webp b/sites/www/content/blog/announcing-flutter-1-20/images/00Xhr32bM0mcyJFXl.webp new file mode 100644 index 0000000000..77c98a912d Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/00Xhr32bM0mcyJFXl.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/01zBQM3EBFDm7gLXC.gif b/sites/www/content/blog/announcing-flutter-1-20/images/01zBQM3EBFDm7gLXC.gif new file mode 100644 index 0000000000..25a4f58fd4 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/01zBQM3EBFDm7gLXC.gif differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/05zRP2E4rqoKmOkxH.webp b/sites/www/content/blog/announcing-flutter-1-20/images/05zRP2E4rqoKmOkxH.webp new file mode 100644 index 0000000000..b35e2f3099 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/05zRP2E4rqoKmOkxH.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/0J4G5VKzvTTxFJaxb.webp b/sites/www/content/blog/announcing-flutter-1-20/images/0J4G5VKzvTTxFJaxb.webp new file mode 100644 index 0000000000..7beb39e9ff Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/0J4G5VKzvTTxFJaxb.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/0LJO_r2jMcO7_49Jj.webp b/sites/www/content/blog/announcing-flutter-1-20/images/0LJO_r2jMcO7_49Jj.webp new file mode 100644 index 0000000000..724db16693 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/0LJO_r2jMcO7_49Jj.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/0PWJq9DpGlqdZkkLH.webp b/sites/www/content/blog/announcing-flutter-1-20/images/0PWJq9DpGlqdZkkLH.webp new file mode 100644 index 0000000000..83326c0f5f Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/0PWJq9DpGlqdZkkLH.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/0Pe9BXnIWEyUpoI-_.webp b/sites/www/content/blog/announcing-flutter-1-20/images/0Pe9BXnIWEyUpoI-_.webp new file mode 100644 index 0000000000..f6082cfc55 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/0Pe9BXnIWEyUpoI-_.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/0QfzssiJe72kCh0LU.webp b/sites/www/content/blog/announcing-flutter-1-20/images/0QfzssiJe72kCh0LU.webp new file mode 100644 index 0000000000..aee56c821f Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/0QfzssiJe72kCh0LU.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/0TcCxp1ljELxXMG8c.webp b/sites/www/content/blog/announcing-flutter-1-20/images/0TcCxp1ljELxXMG8c.webp new file mode 100644 index 0000000000..d3fe513ac6 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/0TcCxp1ljELxXMG8c.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/0Ve1UjCRej9MtX2kv.webp b/sites/www/content/blog/announcing-flutter-1-20/images/0Ve1UjCRej9MtX2kv.webp new file mode 100644 index 0000000000..3e07c14d4b Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/0Ve1UjCRej9MtX2kv.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/0aAYBrux106MX4MxQ.webp b/sites/www/content/blog/announcing-flutter-1-20/images/0aAYBrux106MX4MxQ.webp new file mode 100644 index 0000000000..00b14c73e0 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/0aAYBrux106MX4MxQ.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/0dfvbXEE8UpLj6nGS.webp b/sites/www/content/blog/announcing-flutter-1-20/images/0dfvbXEE8UpLj6nGS.webp new file mode 100644 index 0000000000..02ece74221 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/0dfvbXEE8UpLj6nGS.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/0fI60rwc5QF7eh8jr.webp b/sites/www/content/blog/announcing-flutter-1-20/images/0fI60rwc5QF7eh8jr.webp new file mode 100644 index 0000000000..8e92926bc0 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/0fI60rwc5QF7eh8jr.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/0goluPziG4B5Be-NT.webp b/sites/www/content/blog/announcing-flutter-1-20/images/0goluPziG4B5Be-NT.webp new file mode 100644 index 0000000000..ed29f9ff68 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/0goluPziG4B5Be-NT.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/0hv9zwpJyaf8H-Ssa.webp b/sites/www/content/blog/announcing-flutter-1-20/images/0hv9zwpJyaf8H-Ssa.webp new file mode 100644 index 0000000000..b93efc6530 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/0hv9zwpJyaf8H-Ssa.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/0q-1IbQugDfhrwqDK.webp b/sites/www/content/blog/announcing-flutter-1-20/images/0q-1IbQugDfhrwqDK.webp new file mode 100644 index 0000000000..658acfc27e Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/0q-1IbQugDfhrwqDK.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/0rVf86HRSJ0MyVMB7.webp b/sites/www/content/blog/announcing-flutter-1-20/images/0rVf86HRSJ0MyVMB7.webp new file mode 100644 index 0000000000..2dc2f27380 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/0rVf86HRSJ0MyVMB7.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/0uzKuydE5rOcPqqnX.webp b/sites/www/content/blog/announcing-flutter-1-20/images/0uzKuydE5rOcPqqnX.webp new file mode 100644 index 0000000000..d276d57287 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/0uzKuydE5rOcPqqnX.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/images/1NgNeXqnYnJaekXxEJqSE-w.webp b/sites/www/content/blog/announcing-flutter-1-20/images/1NgNeXqnYnJaekXxEJqSE-w.webp new file mode 100644 index 0000000000..66421d1293 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-20/images/1NgNeXqnYnJaekXxEJqSE-w.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-20/index.md b/sites/www/content/blog/announcing-flutter-1-20/index.md new file mode 100644 index 0000000000..c5c84915f9 --- /dev/null +++ b/sites/www/content/blog/announcing-flutter-1-20/index.md @@ -0,0 +1,218 @@ +--- +title: "Announcing Flutter 1.20" +description: "Performance improvements, mobile autofill, a new widget and more!" +publishDate: 2020-08-05 +author: csells +image: images/0Ve1UjCRej9MtX2kv.webp +category: releases +layout: blog +--- + +Our ongoing vision with Flutter is to provide a portable toolkit for building stunning experiences wherever you might want to paint pixels on the screen. With every release, we continue to push towards ensuring that Flutter is fast, beautiful, productive and open for every platform we support. In Flutter 1.20, which is released today to our stable channel, Flutter has improvements for every one of these four pillars. + +In the category of *fast*, we’ve got multiple performance improvements, from the lowest levels of the rendering engine and in the Dart language itself. + +To enable you to build Flutter apps that are ever more *beautiful*, this release has several UI enhancements, including the long-awaited support for autofill, a new way to layer your widgets to support pan and zoom, new mouse cursor support, updates to old favorite Material widgets (such as the time and date pickers), and a whole new responsive license page for the About box in your desktop and mobile form-factor Flutter apps. + +To make sure that you continue to be more *productive*, we’ve got updates to the [Flutter extension for Visual Studio Code](https://dartcode.org/) that brings Dart DevTools directly into your IDE, automatically updating your import statements as you move your files around and a new set of metadata for building your own tools. + +And it’s because of Flutter’s *openness* and amazing community contributors that this release includes 3,029 merged PRs and 5,485 closed issues from 359 contributors from around the world, including 270 contributors from the Flutter community at large. In fact, this marks the largest number of contributors we’ve ever had for a Flutter release. Special shoutout to community contributor [CareF](https://github.com/CareF) for 28 PRs, [AyushBherwani1998](https://github.com/AyushBherwani1998) for 26 PRs, including 10 to the Flutter samples as part of his Google Summer of Code project, and [a14n](https://github.com/a14n) for 13 PRs, many of which are in service of landing null safety for Flutter (more on that topic soon!). We couldn’t create Flutter without a broad team of community contributors, so thank you! + +Each new release of Flutter brings with it increased usage and momentum. In fact, in April, [we reported](https://medium.com/flutter/flutter-spring-2020-update-f723d898d7af) that the number of Flutter apps in the Google Play store had reached 50,000, with a peak rate of 10,000 new apps/month. Now, just over three months later, there are more than 90,000 Flutter apps in Google Play. We’re seeing a lot of this growth in India, which is now the #1 region for Flutter developers, having doubled in the last six months, which aligns well with [Google’s increased investment](https://www.businessinsider.com/google-alphabet-india-health-agriculture-education-tech-ai-sundar-pichai-2020-7) in that region. And finally, Flutter isn’t Flutter without Dart, so it’s great to see the that [the IEEE has reported that Dart has moved up 4 slots since last year to be #12](https://spectrum.ieee.org/static/interactive-the-top-programming-languages-2020) in the top 50 languages that they track. + +## Performance improvements for Flutter and Dart + +On the Flutter team, we’re always looking for new ways to decrease the size and latency of your app. As an example of the former, this release fixes [a tooling performance issue with icon font tree shaking](https://github.com/flutter/flutter/pull/55417) and [makes font tree shaking the default behavior](https://github.com/flutter/flutter/pull/56633) when building your non-web apps. Icon font tree shaking removes the icons that you’re not using in your app, thus reducing the size. Using this against the Flutter Gallery app, we found that it [reduced the app size by 100kb](https://github.com/flutter/flutter/pull/49737). Now you get this behavior by default in your mobile apps when you’re doing a release build. It’s currently restricted to TrueType Fonts, but that restriction will be lifted in future releases. + +Another performance improvement we’ve made in this release reduces jank in the initial display of your animation using a warm-up phase. You can see an example of the jank improvement in this animation (slowed down to half speed). + +<DashImage figure src="images/0LJO_r2jMcO7_49Jj.webp" alt="animation without and with the SkSL warm-up" caption="animation without and with the SkSL warm-up" /> + + +If a Flutter app has janky animations during the first run, the Skia Shading Language shader provides for pre-compilation as part of your app’s build that can speed it up by more than 2x. If you’d like to take advantage of this advanced functionality, see [the SkSL warm-up page](https://flutter.dev/docs/perf/rendering/shader) on flutter.dev. + +And finally, as we optimize for desktop form-factors, we continue to refine our mouse support. In this release, we’ve [refactored the mouse hit testing system](https://github.com/flutter/flutter/pull/59883) to provide a number of architectural advantages that were blocked due to performance issues. The refactoring enables us to improve the performance by as much as 15x in our web-based microbenchmarks! What this means to you is that you get better, more consistent, and more accurate hit testing w/o giving up performance: win-win! + +With this better, faster, stronger mouse hit testing, we’ve added support for mouse cursors — one of the most upvoted features for desktop. Several commonly used widgets will display the cursors you expect by default, or you can specify another from the list of supported cursors. + +<DashImage figure src="images/01zBQM3EBFDm7gLXC.gif" alt="new mouse cursors over existing widgets on Android" caption="new mouse cursors over existing widgets on Android" /> + + +This release of Flutter is built on the 2.9 release of Dart. This features a new state-based, two-pass UTF-8 decoder with decoding primitives optimized in the Dart VM, partially taking advantage of SIMD instructions. UTF-8 is by far the most widely used character encoding method on the internet, and being able to decode it quickly is critical when receiving large network responses. In our UTF-8 decoding benchmarks we have seen improvements across the board from nearly 200% for English texts to 400% for Chinese texts on low-end ARM devices. + +## Autofill for mobile text fields + +One of the #1 most requested Flutter features for a while has been to support the underlying Android and iOS support for text autofill in Flutter programs. With [PR 52126](https://github.com/flutter/flutter/pull/52126), we’re pleased to say that the wait is over — no more asking your users to re-enter data that the OS has already gathered for them. + +<DashImage figure src="images/0fI60rwc5QF7eh8jr.webp" alt="Autofill in action" caption="Autofill in action" /> + + +You’ll be pleased to hear that we’ve already started adding this functionality for the web, as well. + +## A new widget for common patterns of interaction + +This release introduces a new widget, the `InteractiveViewer`. The `InteractiveViewer` is designed for building common kinds of interactivity into your app, like pan, zoom, and drag ’n’ drop, even in the face of resizing, which [this simple Go board sample](https://github.com/justinmc/flutter-go) demonstrates. + +<DashImage figure src="images/1NgNeXqnYnJaekXxEJqSE-w.webp" alt="Zooming, panning, resizing, dragging and dropping with the `InteractiveViewer`" caption="Zooming, panning, resizing, dragging and dropping with the `InteractiveViewer`" /> + + +To see how to integrate the `InteractiveViewer` into your own app, [check out the API documentation](https://api.flutter.dev/flutter/widgets/InteractiveViewer-class.html) where you can play with it in DartPad. Also, if you’d like to hear about how the `InteractiveViewer` was designed and developed, you can [see a presentation by the author for Chicago Flutter on YouTube](https://www.youtube.com/watch?v=ChFa0A72Uto). + +If you’re interested in adding the kind of interactivity to your Flutter app that `InteractiveViewer` enables, then you’ll probably also be happy to hear that we’ve [added more capabilities to drag ’n’ drop](https://github.com/monkeyswarm/DragTargetDetailsExample) in this release. Specifically, if you’d like to know precisely where the drop happened on the target widget (it’s always been available to the *Draggable* object itself), now you can get that information with the *DragTarget onAcceptDetails* method. + +<DashImage figure src="images/0dfvbXEE8UpLj6nGS.webp" alt="New drag target accept details in action" caption="New drag target accept details in action" /> + + +Check out [this sample](https://github.com/monkeyswarm/DragTargetDetailsExample) for the details and look forward to a future release that will make this information available during the drag as well so that the *DragTarget* can more easily provide visual updates during a drag operation. + +## Updated Material Slider, RangeSlider, TimePicker, and DatePicker + +In addition to new widgets, this release includes a number of updated widgets to match [the latest Material guidelines](https://material.io/components/sliders). These include `Slider` and `RangeSlider`. For more information, see [What’s new with the Slider widget?](https://medium.com/flutter/whats-new-with-the-slider-widget-ce48a22611a3) + +<DashImage figure src="images/0q-1IbQugDfhrwqDK.webp" alt="updated Material Slider" caption="updated Material Slider" /> + + +<DashImage figure src="images/05zRP2E4rqoKmOkxH.webp" alt="Updated Material RangeSlider" caption="Updated Material RangeSlider" /> + + +`DatePicker` has been updated to include a new compact design as well as support for date ranges. + +<DashImage figure src="images/0rVf86HRSJ0MyVMB7.webp" alt="updated DatePicker" caption="updated DatePicker" /> + + +And finally, `TimePicker` has a completely new style. + +<DashImage figure src="images/0Ve1UjCRej9MtX2kv.webp" alt="updated TimePicker" caption="updated TimePicker" /> + + +If you’d like to play around with it, here’s [a fun web demo built with Flutter](https://flutter-time-picker.firebaseapp.com/#/). + +## Responsive Licenses page + +Another update this release is the new responsive licenses page available from the `AboutDialog`. + +<DashImage figure src="images/0goluPziG4B5Be-NT.webp" alt="new licenses page" caption="new licenses page" /> + + +[PR 57588](https://github.com/flutter/flutter/pull/57588), from community contributor [TonicArtos](https://github.com/TonicArtos), is not only updated to match Material guidelines, making it just plain nice to look at, but it’s easier to navigate and designed to work as well on tablets and desktops as on phones. Thanks, TonicArtos! Since every Flutter app should be showing the licenses for the packages they’re using, you just made every Flutter app better! + +## New pubspec.yaml format required for publishing plugins + +Of course, Flutter isn’t just the widgets; it’s also the tooling and this release comes with too many updates to mention. However, here are some of the highlights. + +First and foremost, a public service announcement: if you’re a Flutter plugin author, then the legacy `pubspec.yaml` format is no longer supported for publishing plugins. If you try, you’ll get the following error message when executing *pub publish*: + +<DashImage figure src="images/0PWJq9DpGlqdZkkLH.webp" alt="Legacy pubspec format error message upon plugin publication" caption="Legacy pubspec format error message upon plugin publication" /> + + +The old format did not support specifying which platforms your plugins support, and has been deprecated since Flutter 1.12. [The new `pubspec.yaml` format](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms) is now required for publishing new or updated plugins. + +For clients of plugins, the tools still understand the old pubspec format and will for the foreseeable future. All existing plugins on pub.dev using the legacy `pubspec.yaml` format will continue to work with Flutter apps for the foreseeable future. + +## Preview of embedded Dart DevTools in Visual Studio Code + +The biggest tooling update in this release comes to the Visual Studio Code extension, which provides a preview of a new feature to enable you to bring Dart DevTools screens directly into your coding workspace. + +<DashImage figure src="images/0aAYBrux106MX4MxQ.webp" alt="Preview of Layout Explorer from Dart DevTools embedded into Visual Studio Code" caption="Preview of Layout Explorer from Dart DevTools embedded into Visual Studio Code" /> + + +Enable this feature with the new *dart.previewEmbeddedDevTools* setting. The above screenshot shows the Flutter Widget Inspector embedded directly into Visual Studio Code but with this new setting enabled, you can choose your favorite page embed using the Dart DevTools menu on the status bar. + +<DashImage figure src="images/0uzKuydE5rOcPqqnX.webp" /> + + +This menu allows you to choose which pages to show. + +<DashImage figure src="images/0TcCxp1ljELxXMG8c.webp" /> + + +This feature is still in preview, so [let us know if you have any trouble with it](https://github.com/Dart-Code/Dart-Code/issues). + +## Updates to network tracking + +The latest version of Dart DevTools comes with an updated version of the Network page that enables web socket profiling. + +<DashImage figure src="images/0hv9zwpJyaf8H-Ssa.webp" alt="Timing, status and content type of socket connections on the Network page of Dart DevTools" caption="Timing, status and content type of socket connections on the Network page of Dart DevTools" /> + + +The Network page now adds timing information to the network calls from your app, along with other information like status and content type. Additional improvements have been made to the details UI to provide an overview of the data in a websocket or http request. We’ve also got more plans for this page to include HTTP request/response bodies and monitoring gRPC traffic. + +## Updating import statements on file rename + +Another new feature for Visual Studio Code is updating imports on rename, which automatically updates *import* statements when files are moved or renamed. + +<DashImage figure src="images/00Xhr32bM0mcyJFXl.webp" alt="moving Dart files in Visual Studio Code updates the import statements" caption="moving Dart files in Visual Studio Code updates the import statements" /> + + +This feature currently only works for single files and not multiple files or folders, but that support is coming soon. + +## Tooling metadata for every tool builder + +One more update to mention is for people building Flutter tooling. We’ve created a new project on GitHub to capture and publish metadata about the Flutter framework itself. It provides machine-readable data files for the following: + +* a [catalog](https://github.com/flutter/tools_metadata/blob/master/resources/catalog/widgets.json) of all of the current Flutter widgets (395 widgets!) + +* a [mapping of Flutter framework color names to color values](https://github.com/flutter/tools_metadata/tree/master/resources/colors), for both the Material and Cupertino color sets + +* [Icon metadata](https://github.com/flutter/tools_metadata/tree/master/resources/icons) for Material and Cupertino icons, including icon names and preview icons + +This is the same metadata that we use for the Android Studio / IntelliJ and VS Code extensions ourselves; we thought you might find it useful when building your own tools. In fact, this metadata enables the feature in the IntelliJ family of IDEs to show the color being used in your Flutter code: + +<DashImage figure src="images/0QfzssiJe72kCh0LU.webp" /> + + +Related to that is a new feature in IntelliJ and Android Studio that displays color blocks for Color.fromARGB() and Color.fromRGBO(): + +<DashImage figure src="images/0J4G5VKzvTTxFJaxb.webp" /> + + +Special thanks to [dratushnyy](https://github.com/dratushnyy) on GitHub for contributing improvements to the color previews in IntelliJ! + +## Typesafe platform channels for platform interop + +In response to popular demand from plugin authors in our user surveys, recently we’ve been experimenting on how to make communication between Flutter and the host platform safer and easier for [plugins](https://flutter.dev/docs/development/packages-and-plugins/developing-packages) and [Add-to-App](https://flutter.dev/docs/development/add-to-app). To address this need, we created [Pigeon](https://pub.dev/packages/pigeon), a command-line tool that uses Dart syntax to generate type-safe messaging code on top of platform channels without adding additional runtime dependencies. With Pigeon, instead of manually matching method strings on platform channels and serializing arguments, you can invoke Java/Objective-C/Kotlin/Swift class methods and pass non-primitive data objects by directly calling Dart methods (and vice versa). + +<DashImage figure src="images/0Pe9BXnIWEyUpoI-_.webp" /> + + +While still in prerelease, Pigeon has become mature enough that we’re using it ourselves in the [video_player](https://pub.dev/packages/video_player) plugin. If you’d interested in testing out Pigeon for your own uses, see the updated the [platform channel documentation](https://flutter.dev/docs/development/platform-integration/platform-channels#pigeon) as well as this [sample project](https://github.com/flutter/samples/tree/master/add_to_app/flutter_module_books). + +## Too many tooling updates to list + +So much great stuff has happened to the tools in the Flutter 1.20 timeframe that we can’t list it all here. However, you might want to take a look at the update announcements themselves: + +* [VS Code extensions v3.13](https://groups.google.com/g/flutter-announce/c/TlN12RemsYw) + +* [VS Code extensions v3.12](https://groups.google.com/g/flutter-announce/c/8tSufvaRJUg) + +* [VS Code extensions v3.11](https://groups.google.com/g/flutter-announce/c/gM0bqO7NFA0) + +* [Flutter IntelliJ Plugin M46 Release](https://groups.google.com/g/flutter-announce/c/8C2v2ueXjts) + +* [Flutter IntelliJ Plugin M47 Release](https://groups.google.com/g/flutter-announce/c/6SF3PG_XB8g/m/6mAY7eC_AAAJ) + +* [Flutter IntelliJ Plugin M48 Release](https://groups.google.com/g/flutter-announce/c/i9NTk5o9rZQ) + +* [New tools for Flutter developers, built in Flutter](https://medium.com/flutter/new-tools-for-flutter-developers-built-in-flutter-a122cb4eec86) + +## Breaking Changes + +As ever, we try to keep the number of breaking changes low. Here’s the list from the Flutter 1.20 release. + +* [55336](https://github.com/flutter/flutter/pull/55336) Adding tabSemanticsLabel to CupertinoLocalizations — [Migration guide PR](https://github.com/flutter/website/pull/3996) + +* [55977](https://github.com/flutter/flutter/pull/55977) [Add clipBehavior to widgets with clipRect](https://flutter.dev/go/clip-behavior) + +* [55998](https://github.com/flutter/flutter/pull/55998) [Fixes the navigator pages update crashes when there is still route wa…](https://groups.google.com/forum/#!searchin/flutter-announce/55998%7Csort:date/flutter-announce/yoq2VGi94q8/8pTsRL28AQAJ) + +* [56582](https://github.com/flutter/flutter/pull/56582) [Update Tab semantics in Cupertino to be the same as Material](https://flutter.dev/docs/release/breaking-changes/cupertino-tab-bar-localizations#migration-guide) + +* [57065](https://github.com/flutter/flutter/pull/57065) Remove deprecated child parameter for NestedScrollView’s overlap managing slivers + +* [58392](https://github.com/flutter/flutter/pull/58392) iOS mid-drag activity indicator + +## Summary + +Hopefully, you’re as excited about this release as we are. From many angles, this is Flutter’s biggest release yet. With performance improvements, new and updated widgets, and tooling improvements, we can only hit the highlights. We want to thank you, the strong and growing set of community contributors that enables every Flutter release to be bigger, faster, and stronger than the one before. And there’s more to come, with support for [null safety](http://dart.dev/null-safety), a new version of the Ads, Maps, and WebView plugins, and more tooling support in the works. (In fact, you might be interested in Bob Nystrom’s deep dive on [Understanding null safety](https://dart.dev/null-safety/understanding-null-safety).) + +With all of this extra power in Flutter and the tools, what are you going to build? \ No newline at end of file diff --git a/sites/www/content/blog/announcing-flutter-1-22/images/0-FukCwELl9DLlefP.webp b/sites/www/content/blog/announcing-flutter-1-22/images/0-FukCwELl9DLlefP.webp new file mode 100644 index 0000000000..5d23ccfe46 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-22/images/0-FukCwELl9DLlefP.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-22/images/01298qhTKz_FjPuRN.webp b/sites/www/content/blog/announcing-flutter-1-22/images/01298qhTKz_FjPuRN.webp new file mode 100644 index 0000000000..de6e1e79b9 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-22/images/01298qhTKz_FjPuRN.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-22/images/08MER4IM-IEt2ye2a.webp b/sites/www/content/blog/announcing-flutter-1-22/images/08MER4IM-IEt2ye2a.webp new file mode 100644 index 0000000000..4eed6d9180 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-22/images/08MER4IM-IEt2ye2a.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-22/images/0DFRjYzbqIc5kOoDm.webp b/sites/www/content/blog/announcing-flutter-1-22/images/0DFRjYzbqIc5kOoDm.webp new file mode 100644 index 0000000000..186fe72e15 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-22/images/0DFRjYzbqIc5kOoDm.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-22/images/0OVbaUaMkAXrpLQCy.webp b/sites/www/content/blog/announcing-flutter-1-22/images/0OVbaUaMkAXrpLQCy.webp new file mode 100644 index 0000000000..9dd69a4ddf Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-22/images/0OVbaUaMkAXrpLQCy.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-22/images/0RQZ-EVyFP1-BqBIX.webp b/sites/www/content/blog/announcing-flutter-1-22/images/0RQZ-EVyFP1-BqBIX.webp new file mode 100644 index 0000000000..52f446b246 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-22/images/0RQZ-EVyFP1-BqBIX.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-22/images/0UBVlGmhw5NUWbg5I.webp b/sites/www/content/blog/announcing-flutter-1-22/images/0UBVlGmhw5NUWbg5I.webp new file mode 100644 index 0000000000..8eeb5a6593 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-22/images/0UBVlGmhw5NUWbg5I.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-22/images/0ZGKu3IhnPNp6fS_e.webp b/sites/www/content/blog/announcing-flutter-1-22/images/0ZGKu3IhnPNp6fS_e.webp new file mode 100644 index 0000000000..60e2750070 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-22/images/0ZGKu3IhnPNp6fS_e.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-22/images/0gA2waLDAyITCzD7K.webp b/sites/www/content/blog/announcing-flutter-1-22/images/0gA2waLDAyITCzD7K.webp new file mode 100644 index 0000000000..5f4bc6fa4d Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-22/images/0gA2waLDAyITCzD7K.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-22/images/0k-fEpOpbV_MiX9Fn.webp b/sites/www/content/blog/announcing-flutter-1-22/images/0k-fEpOpbV_MiX9Fn.webp new file mode 100644 index 0000000000..4dc2b3ee65 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-22/images/0k-fEpOpbV_MiX9Fn.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-22/images/0lPO4ueKwxvV0QP_I.webp b/sites/www/content/blog/announcing-flutter-1-22/images/0lPO4ueKwxvV0QP_I.webp new file mode 100644 index 0000000000..50e1eadd7b Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-22/images/0lPO4ueKwxvV0QP_I.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-22/images/0luz0EiazQQqsAEC4.webp b/sites/www/content/blog/announcing-flutter-1-22/images/0luz0EiazQQqsAEC4.webp new file mode 100644 index 0000000000..2e6243ee04 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-22/images/0luz0EiazQQqsAEC4.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-22/images/0prigrwpp7gE6ChCE.webp b/sites/www/content/blog/announcing-flutter-1-22/images/0prigrwpp7gE6ChCE.webp new file mode 100644 index 0000000000..84680b8991 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-22/images/0prigrwpp7gE6ChCE.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-22/images/0vfMD0AQZUOUwzfZ4.webp b/sites/www/content/blog/announcing-flutter-1-22/images/0vfMD0AQZUOUwzfZ4.webp new file mode 100644 index 0000000000..8c3c5821ef Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-22/images/0vfMD0AQZUOUwzfZ4.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-22/images/0yLWTxV_YTJ2HKhuz.webp b/sites/www/content/blog/announcing-flutter-1-22/images/0yLWTxV_YTJ2HKhuz.webp new file mode 100644 index 0000000000..866e546e74 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-22/images/0yLWTxV_YTJ2HKhuz.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-22/images/19pvyafhpaMEMhJfLVsz1JQ.webp b/sites/www/content/blog/announcing-flutter-1-22/images/19pvyafhpaMEMhJfLVsz1JQ.webp new file mode 100644 index 0000000000..c5f6794d54 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-22/images/19pvyafhpaMEMhJfLVsz1JQ.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-22/images/1crNLxg7CCtlQkB4mLI_ErA.webp b/sites/www/content/blog/announcing-flutter-1-22/images/1crNLxg7CCtlQkB4mLI_ErA.webp new file mode 100644 index 0000000000..3938f95b20 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-22/images/1crNLxg7CCtlQkB4mLI_ErA.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-22/images/1di6uPEKEsRHoFtRryHeWPg.webp b/sites/www/content/blog/announcing-flutter-1-22/images/1di6uPEKEsRHoFtRryHeWPg.webp new file mode 100644 index 0000000000..7f7bd212d6 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-22/images/1di6uPEKEsRHoFtRryHeWPg.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-22/index.md b/sites/www/content/blog/announcing-flutter-1-22/index.md new file mode 100644 index 0000000000..a30e0b32a4 --- /dev/null +++ b/sites/www/content/blog/announcing-flutter-1-22/index.md @@ -0,0 +1,410 @@ +--- +title: "Announcing Flutter 1.22" +description: "Supporting iOS 14 and Android 11, new i18n and l10n support, Google Maps and WebView plugins ready for production, a new App Size tool and…" +publishDate: 2020-10-01 +author: csells +image: images/0luz0EiazQQqsAEC4.webp +category: releases +layout: blog +--- + +### Supporting iOS 14 and Android 11, new i18n and l10n support, Google Maps and WebView plugins ready for production, a new App Size tool and much more! + +We’re delighted to introduce our latest release of Flutter, with extensive support for iOS 14 and Android 11. Flutter 1.22 builds on the foundation set by previous releases by enabling developers to build fast, beautiful user experiences for multiple platforms from a single codebase. Our quarterly stable releases package the latest features, performance improvements, and bug fixes, and are suitable for broad production use. + +Since this is the season for new mobile OS versions, this release focuses on ensuring that Android 11 and iOS 14 work great with Flutter. Updates for both of these OSes include a lot of under-the-hood work to conform to the latest SDKs and to make sure everything passes our extensive test suite. For iOS 14, this release includes support for the new Xcode 12, new icons, and preview support for the new iOS 14 App Clips feature. For Android 11, the update supports the new types of display cutouts as well as smoother animation when bringing up the soft keyboard. + +This release comes two months after our 1.20 release, so it was shorter than most. Even in that short time, we closed 3,024 issues and merged 1,944 PRs from 197 contributors. Of those contributors, 114 (58%) of them were from the community-at-large and they contributed 271 PRs. The largest single contributor was [a14n](https://github.com/a14n), who makes our top contributor list again with 20 PRs, most of which were done as part of the work to support null safety in Flutter (more on that coming soon). + +In addition to the support for the new mobile OS versions, there’s quite a bit more news to share, including a preview of one of the top-requested features for Android: state restoration, a new “universe” of Material buttons, new international and localization support that works with hot reload, a new Navigator, a stable release for Platform Views (the foundation for the Google Maps and WebView plugins), and a switch you can throw in your code to improve scrolling on devices with high frequency displays. We’ve also got a new tool for dissecting app size and for ensuring that the plugins that you’re building support only the platforms that you want to support. + +## Targeting iOS 14 + +Whenever a new version of a mobile OS is announced, we test it thoroughly, looking for incompatibilities or changes that affect Flutter and its tools. + +In the case of iOS 14, we made quite a few changes to Flutter to ensure that it works the way developers want: + +* Xcode 12 requires iOS 9.0 or up, so our default template increases its default from 8.0 to 9.0 + +* iOS 14 specific crashes and font rendering issues were fixed in Flutter 1.22 + +* Problems deploying to physical devices were fixed as of Flutter 1.20.4 + +* A new policy that shows uses notifications when apps access their clipboard caused spurious notifications in Flutter apps, and was fixed as of Flutter 1.20.4 + +* A restriction disables running debug apps on iOS 14 devices except as part of the debugging process + +* A new policy around network security for locally debugged Flutter apps causes iOS 14 to show a one-time confirmation dialog (only during development, not for released Flutter apps) + +Bottom line: if you’re targeting iOS 14 with your Flutter app, we strongly encourage you to rebuild it with Flutter 1.22 and deploy it to the App Store now to ensure that your iOS 14 users have the best experience. + +For more details about targeting iOS 14 with Flutter, including some Add-to-App, deep linking, and notification considerations, refer to [the iOS 14 documentation on flutter.dev](https://flutter.dev/docs/development/ios-14). + +Hopefully, all of this work on the tooling and SDK support allows you to focus on the coding that you care about — taking advantage of new iOS 14 features. + +One such feature is updated support for iOS’s new SF Symbols font, which inspired us to spend some time giving [the `cupertino_icon` package](https://pub.dev/packages/cupertino_icons) a refresh. Existing uses of `CupertinoIcons` will automatically map to the new style once you update your `cupertino_icons` dependency to the new 1.0 major version. If you use `cupertino_icons` 1.0 in conjunction with Flutter 1.22, you’ll also have access to ~900 new icons through the [`CupertinoIcons`](https://api.flutter.dev/flutter/cupertino/CupertinoIcons-class.html) API. + +<DashImage figure src="images/0ZGKu3IhnPNp6fS_e.webp" /> + + +You can see the complete list of icons on the [`cupertino_icons`](https://flutter.github.io/cupertino_icons/) preview page and [a migration detail page on flutter.dev](https://flutter.dev/docs/release/breaking-changes/cupertino-icons-1.0.0). + +Another feature for you to try with Flutter on iOS 14 is [App Clips](https://developer.apple.com/app-clips/), a new iOS 14 feature that supports quick, no-install app executions of lightweight versions of apps under 10MB. In Flutter version 1.22, we have a preview of App Clip targets built with Flutter. + +<DashImage figure src="images/08MER4IM-IEt2ye2a.webp" alt="A Flutter-powered App Clip experience" caption="A Flutter-powered App Clip experience" /> + + +For more details on how to build App Clips with Flutter, check out [the docs on flutter.dev](https://flutter.dev/docs/development/platform-integration/ios-app-clip). You could also consult this [simple sample project](https://github.com/flutter/samples/tree/master/ios_app_clip). + +## Android 11 + +This release of Flutter also coincides with the launch of Android 11 this month. The Flutter framework and engine have been updated to support two new features introduced in the latest version of Android. + +Firstly, Flutter now supports exposing the safe insets of Android notches, cutouts and edges of waterfall displays. + +<DashImage figure src="images/0lPO4ueKwxvV0QP_I.webp" /> + + +By using the [`MediaQuery`](https://api.flutter.dev/flutter/widgets/MediaQuery-class.html) and [`SafeArea`](https://api.flutter.dev/flutter/widgets/SafeArea-class.html) APIs, you can ensure that you’re placing active UI and interactive elements in the non-obstructed regions of the device’s display. Also, you’ll want to avoid gesture detectors in the waterfall edge area that may be prone to accidental touches. + +Secondly, the animation is synchronized with Android 11 as it displays the software keyboard. + +<DashImage figure src="images/0DFRjYzbqIc5kOoDm.webp" alt="See the position animation of the FAB" caption="See the position animation of the FAB" /> + + +Issue [#19279](https://github.com/flutter/flutter/issues/19279) has been a long-standing problem where the system keyboard show/hide animation isn’t synchronized with Flutter’s inset. This is fixed for Android 11. + +One note about the Android embedding API. With Flutter version 1.12 last year, we rolled out a new set of Flutter engine and Flutter plugin APIs for Android. We created these v2 APIs to better support our add-to-app users on Android. A year later, over 80% of our Android plugins use the new Android APIs. Starting in 1.22, we’re deprecating the older v1 APIs. + +If you’re still using the Android v1 API, here’s what this means for you: + +* Newly created plugins will no longer target the v1 APIs + +* The Flutter tool’s `—no-enable-android-embedding-v2` config flag has been removed and is now the default behavior + +* Older applications still using the v1 APIs will show a deprecation warning during build that points to the [Supporting the new Android plugins APIs](https://flutter.dev/docs/development/packages-and-plugins/plugin-api-migration) docs + +Meanwhile, if you still have a Flutter application based on v1 Android APIs, it will continue to work. However, you may start to encounter new plugins that only target the v2 API and that can’t be consumed by v1 Android APIs. For more details, see [the breaking change documentation](https://flutter.dev/docs/release/breaking-changes/android-v1-embedding-create-deprecation). + +## Expanding the Button “universe” + +<DashImage figure src="images/1crNLxg7CCtlQkB4mLI_ErA.webp" alt="A new universe of Material Design buttons" caption="A new universe of Material Design buttons" /> + + +Existing Flutter buttons look good but can be [hard to use](http://flutter.dev/go/material-button-system-updates), especially when you need custom theming. Furthermore, the Material specification has expanded to include new buttons with new styles. + +To keep Flutter up to date with Material guidelines, we’re happy to announce a whole new “universe” of buttons in Flutter 1.22. + +Rather than try and evolve the existing button classes and their theme in-place, [this PR](https://github.com/flutter/flutter/pull/59702) introduces new, replacement button widgets and themes. In addition to freeing us from the backwards compatibility labyrinth that evolving the existing classes would entail, the new names sync Flutter with the [Material Design spec](https://material.io/components/buttons/), which uses the new names for the button components. + +<DashImage figure src="images/1di6uPEKEsRHoFtRryHeWPg.webp" /> + + +The new themes follow the “normalized” pattern that Flutter has recently adopted for new Material widgets. If you’d like to play with a demo, there’s [a great one here on DartPad](https://dartpad.dev/e560e1c2e4455ad53aac245079ccdcf2). This is not a breaking change as the semantics of `FlatButton`, `OutlineButton`, `RaisedButton`, `ButtonBar`, `ButtonBarTheme`, and `ButtonTheme` won’t change. You can mix and match the old buttons with the new. as you prefer. + +## New internationalization and localization support + +Flutter has provided the core functionality you need for the internationalization (i18n) and localization (l10n) of your apps since Flutter’s inception. However, with this release, we’ve baked our opinions of best practices into our tools, even enabling hot reload support to update your app as you add new l10n information. + +<DashImage figure src="images/0UBVlGmhw5NUWbg5I.webp" /> + + +If you’d like more details about Flutter’s support for l10n, including localized messages, messages with parameters, dates, numbers, and currencies, [read the Flutter Internationalization User Guide](http://flutter.dev/go/i18n-user-guide). + +Furthermore, if you’re interested in i18n and l10n, then you’re probably also interested in strings with characters that don’t fit into plain old ASCII, like Unicode and emoji. Recently, the Dart team released [the `characters` package](https://pub.dev/packages/characters), which helps developers deal with Unicode (extended) grapheme clusters. This package helps solve problems like how to properly abbreviate a string like “A 🇬🇧 text in English” to the first 15 characters. Using the `String` class, that abbreviation would be “A 🇬🇧 text in”, which is only 12 user-perceived characters. On the other hand, using the `characters` package yields the correct abbreviation of “A 🇬🇧 text in Eng”. + +With [this PR](https://github.com/flutter/flutter/pull/59267), Flutter uses the characters package to properly handle these complex characters. For example, when using a `TextField` with a `maxLength` limit, characters like 👨‍👩‍👦 are now properly counted as a single character. Also, with [this PR](https://github.com/flutter/flutter/pull/59620), the characters package is automatically available in projects wherever Flutter is, without needing to manually add it. Hopefully this makes it even easier to handle strings of all kinds from all locales. For more details about the characters package, check out the excellent article, [Dart string manipulation done right 👉](https://medium.com/dartlang/dart-string-manipulation-done-right-5abd0668ba3e). + +## Google Maps and WebView plugins ready for production + +Here on the Flutter team, we are often cautious about labeling something as “production ready” until we’ve tested it thoroughly ourselves. In the case of the [`google_maps_flutter`](https://pub.dev/packages/google_maps_flutter) and [`webview_flutter`](https://pub.dev/packages/webview_flutter) plugins, the gating factor has been the underlying [Platform Views](http://flutter.dev/docs/development/platform-integration/platform-views) implementation, which allows native UI components from both Android and iOS to be hosted in a Flutter app. With this release of Flutter, we’re happy to announce that we have hardened the framework plumbing enough to declare both of these plugins as production ready. + +<DashImage figure src="images/0luz0EiazQQqsAEC4.webp" alt="webview_flutter plugin hosting flutter.dev" caption="webview_flutter plugin hosting flutter.dev" /> + + +In Flutter 1.22, we’ve added an alternative Platform Views implementation that fixes [all known keyboard, and accessibility issues for Android views](https://github.com/flutter/flutter/issues/61133). Also, it works with Android API level 19 and above (it used to require level 20). We have also made threading improvements on iOS that make platform views more efficient and more robust (and no longer requires you to add the `io.flutter.embedded_views_preview` flag to your iOS `Info.plist`)*.* + +The `webview_flutter` plugin supports the new Android Platform Views mode but currently it needs to be [enabled manually](https://github.com/flutter/plugins/blob/master/packages/webview_flutter/README.md#android). We’ll enable it by default in future versions once it’s gotten more use in the wider community. + +The Google Maps and WebView plugins already benefit from the improvements in Platform Views. If you’d like to use Platform Views to host your own native UI components on iOS or Android, you can learn how to do so on [Hosting native Android and iOS views in your Flutter app with Platform Views](https://flutter.dev/docs/development/platform-integration/platform-views). + +## Navigator 2.0 + +If you’ve used [navigation](https://flutter.dev/docs/development/ui/navigation) in your Flutter apps before, you may have noticed that the core data structure, the stack of pages that your user is navigating through, is hidden from you. Instead, to manage it, you call `Navigator.pop()` **or `Navigator.push()`. For example, let’s say that you wanted to show a list of widgets on your home page and allow the user to tap on one to get to a detail page dedicated to just that color. + +<DashImage figure src="images/0OVbaUaMkAXrpLQCy.webp" /> + + +The two screens could be implemented like so: + +```dart +class ColorListScreen extends StatelessWidget { + final List<Color> colors; + final void Function(Color color) onTapped; + ColorListScreen({this.colors, this.onTapped}); + + @override + Widget build(BuildContext context) => Scaffold( + appBar: AppBar(title: Text('Colors')), + body: Column( + children: [ + // you can see and decide on every color in this list + for (final color in colors) + Expanded( + child: GestureDetector( + child: Container(color: color), + onTap: () => onTapped(color), + ), + ) + ], + ), + ); +} + +class ColorScreen extends StatelessWidget { + final Color color; + const ColorScreen({this.color}); + + @override + Widget build(BuildContext context) => Scaffold( + appBar: AppBar(title: Text('Color')), + body: Container(color: color), + ); +} +``` + +Using the simplest Navigator 1.0 style allows you to navigate between these two screens in a way that looks pretty easy: + +```dart +class _ColorAppState extends State<ColorApp> { + List<Color> _colors = [Colors.red, Colors.green, Colors.blue]; + + @override + Widget build(BuildContext context) => MaterialApp( + title: 'Color App', + home: Builder( + builder: (context) => ColorListScreen( + colors: _colors, + // the Navigator manages the list of pages itself; you can only push and pop + onTapped: (color) => Navigator.push( + context, + MaterialPageRoute(builder: (context) => ColorScreen(color: color)), + ), + ), + ), + ); +} + +``` + +The call to `Navigator.push()` is all that’s needed to push another page on top of the first one, creating a stack of two pages. However, unlike the list of `Containers` created in the build method of the `ColorListScreen`, that stack is hidden from you. And because it’s hidden, it’s hard to manage for other scenarios, like handling deep linking with an initial route provided by a native embedding, for example, or a URL from the web or intent from Android. It’s also exceedingly difficult to manage nested routing between different arrangements of the same page. + +Navigator 2.0 solves these problems and more by making the stack of pages visible. Here’s an updated example of navigating between the same `ColorListScreen` and `ColorScreen`: + +```dart + +class _ColorAppState extends State<ColorApp> { + Color _selectedColor; + List<Color> _colors = [Colors.red, Colors.green, Colors.blue]; + + @override + Widget build(BuildContext context) => MaterialApp( + title: 'Color App', + home: Navigator( + // you can see and decide on every page in this list + pages: [ + MaterialPage( + child: ColorListScreen( + colors: _colors, + onTapped: (color) => setState(() => _selectedColor = color), + ), + ), + if (_selectedColor != null) MaterialPage(child: ColorScreen(color: _selectedColor)), + ], + onPopPage: (route, result) { + if (!route.didPop(result)) return false; + setState(() => _selectedColor = null); + return true; + }, + ), + ); +} + +``` + +The app explicitly creates a `Navigator` and gives it a list of pages that represents the complete stack. We create an empty `_selectedColor` to indicate that no color has yet been selected, so we don’t show the `ColorScreen` initially. When the user selects a color, we call `setState()` as normal to indicate to Flutter that you’d like the `build()` method called again, which now creates a stack with the `ColorScreen` on top. + +You update your state in the `OnPopPage` call back, for example, if the user popped, then they’ve “unselected” the current color and we no longer want to show that page. + +If Navigator 2.0 looks like the rest of Flutter, that’s the intent — it’s declarative, unlike Navigator 1.0 which is imperative. The idea was to unify the models between navigation and the rest of Flutter while simultaneously fixing a bunch of issues and adding features. In fact, this little example barely scratches the surface of what’s in Navigator 2.0. For the details, I highly recommend the article on [Declarative navigation and routing in Flutter](https://medium.com/flutter/learning-flutters-new-navigation-and-routing-system-7c9068155ade). + +Also, your existing use of Navigator 1.0 will continue to work just as it does today and won’t be removed anytime soon. If you prefer that model, you can certainly continue to use it. However, if you try Navigator 2.0, we think you’ll like it. + +## Preview: State Restoration for Android + +A new feature that is available for you to experiment with in this release is support for [State Restoration on Android](https://developer.android.com/topic/libraries/architecture/saving-states). This is one of our [most-requested features](https://github.com/flutter/flutter/issues/6827) with 217 thumbs up! + +For those not familiar with the need for state restoration, mobile OSes might kill apps that are in the background to reclaim resources for foreground apps. When this happens, the OS notifies the app to be killed to save any UI state quickly so that it can be restored when the user cycles back to that app. When implemented correctly, this provides a seamless experience for the user while making better use of the device’s resources. Until now, Flutter didn’t support state restoration and it was very difficult to do it correctly without framework support. That’s why we’re very happy to be able to deliver the foundational implementation of this feature for Android. + +Here’s [a very simple sample](https://api.flutter.dev/flutter/widgets/RestorationMixin-mixin.html#widgets.RestorationMixin.1) for restoring the state of the default Flutter Counter app: + +```dart +class CounterState extends State<RestorableCounter> with RestorationMixin { + @override + String get restorationId => widget.restorationId; + + RestorableInt _counter = RestorableInt(0); + + @override + void restoreState(RestorationBucket oldBucket) => registerForRestoration(_counter, 'count'); + + void _incrementCounter() => setState(() => _counter.value++); + + @override + Widget build(BuildContext context) => Scaffold( + body: Center(child: Text('${_counter.value}')), + floatingActionButton: FloatingActionButton(onPressed: _incrementCounter), + ); +} + +``` + +Briefly, each widget gets a storage bucket, which is registered with the [`RestorationMixin`](https://api.flutter.dev/flutter/widgets/RestorationMixin-mixin.html) using a unique ID. By using a [`RestorableProperty`](https://master-api.flutter.dev/flutter/widgets/RestorableProperty-class.html) type (like `RestorableInt` used here) to store the UI-specific data, and by registering that data with the State Restoration feature, the data is automatically stored before Android kills the app, and restored when it’s brought back to life. And that’s it. Any data that’s stored in a `Restoration*` type, like `RestorableInt`, `RestorableString` and `RestorableTextEditingController` (we’ve got a bunch of them) will be restored. And if we don’t cover all of the types you’d like to have restored, you can create your own by extending [`RestorableProperty<T>`](https://api.flutter.dev/flutter/widgets/RestorableProperty-class.html). + +<DashImage figure src="images/01298qhTKz_FjPuRN.webp" /> + + +For automated testing of state restoration, we’ve added [a new `restartAndRestore` API to WidgetTester](https://api.flutter.dev/flutter/flutter_test/WidgetTester/restartAndRestore.html). And to test manually, the easiest thing to do is to start your state restoration-enabled Flutter app on an Android device, enable “Don’t keep activities” in Android’s developer settings, run your Flutter app, put it into the background, and then return to it. At this point, Android will have killed and restored your app, so you can see if everything is working as you expect. + +While we’re happy to put this preview version of State Restoration in your hands, there’s more work to do. For example, state restoration isn’t just for Android, iOS apps could benefit as well. Furthermore, we’re busy updating our own widgets to keep their state during restoration. We’ve already provided support in the `Scrollable` classes like `ListView` and `SingleChildScrollView` (to remember the user’s scroll position) and `TextFields` (to restore the text they’ve entered), and we plan to extend that to other widgets. + +However, the key restoration support we have not yet added, and what makes this a preview release, is navigation (either 1.0 or 2.0). This means that where your user is in the app won’t be restored. That feature will be coming to a beta soon and to the next stable release of Flutter. + +## Preview: Smooth scrolling for unmatched input and display frequencies + +Working with our internal Google partners, the Flutter team has greatly improved the performance of scrolling when the input and the display frequencies are not the same. For example, the Pixel 4 input runs at 120hz whereas the display runs at 90hz. This mismatch can cause a performance penalty when scrolling. With a new `resamplingEnabled` flag, you can take advantage of the performance work we’ve done in Flutter to address this issue: + +```dart +void main() { + GestureBinding.instance.resamplingEnabled = true; + run(MyApp()); +} +``` + +Depending on the frequency discrepancies involved, you may see up to 97% less jank in scrolling by enabling this flag. When we’re sure this is the best experience, we plan to enable this flag by default in a future build. + +## A new unified Dart developer tool + +As always, an update to Flutter doesn’t just mean the engine and the framework but the tooling as well. Flutter 1.22 includes a new version of Dart (2.10), and there’s a new `dart` CLI tool that you might find useful as well. + +Dart historically has had many smaller developer tools (such as `dartfmt` for formatting, and `dartanalyzer` for code analysis). New in Dart 2.10 is a unified `dart` developer tool very similar to the `flutter` tool. + +<DashImage figure src="images/19pvyafhpaMEMhJfLVsz1JQ.webp" /> + + +Starting with today’s Flutter 1.22 SDK, you will find that the `<flutter-sdk>/bin` folder (which you likely have in your`PATH`) contains both `flutter` and `dart` commands. For more details, see the [Dart 2.10 blog post](https://medium.com/@mit.mit/announcing-dart-2-10-350823952bd5). + +## App size analysis tool + +The tooling released as part of Flutter 1.22 includes a new output size analysis utility. This tool helps diagnose Flutter whether the breakdown of your app’s size changes over time. + +You can use the tool to gather the data necessary for analysis by passing an`--analyze-size` flag to any of the following commands: + +* `flutter build apk` + +* `flutter build appbundle` + +* `flutter build ios` + +* `flutter build linux` + +* `flutter build macos` + +* `flutter build windows` + +Using this flag while building a Flutter output artifact prints a summary of the artifact’s size and composition. This includes native code, assets, and even a package-level breakdown of compiled Dart code. + +<DashImage figure src="images/0vfMD0AQZUOUwzfZ4.webp" alt="Example breakdown of the Flutter Gallery’s release APK" caption="Example breakdown of the Flutter Gallery’s release APK" /> + + +This summary is helpful in quickly identifying hotspots in the application’s package size usage. In addition, the gathered data is also available as a JSON file for use in Dart DevTools, which allows you to further explore your app’s contents, pinpoint size issues and see changes between two different JSON files by following [the instructions on flutter.dev](https://flutter.dev/docs/development/tools/devtools/app-size). Once you load the JSON file, you’ll have an interface that gives you a tree map view of your app’s size. + +<DashImage figure src="images/0gA2waLDAyITCzD7K.webp" alt="An example APK breakdown in Dart DevTools" caption="An example APK breakdown in Dart DevTools" /> + + +For more details of the things you can do with the App Size tool, read [the Using the app size tool docs](https://flutter.dev/docs/development/tools/devtools/app-size) on flutter.dev. + +## Preview: Updated Network Page in DevTools + +Another DevTools preview feature in this release is the ability to see HTTP and HTTPs response bodies as part of the **Network** tab. + +<DashImage figure src="images/0-FukCwELl9DLlefP.webp" /> + + +To enable this feature, make sure you’re on the Flutter dev channel via `flutter channel dev` and `flutter channel upgrade`. + +In addition, for apps with lots of network traffic, we’ve provided the ability to search and filter. + +<DashImage figure src="images/0k-fEpOpbV_MiX9Fn.webp" /> + + +For the **Network** tab docs, see [Using the Network View](https://flutter.dev/docs/development/tools/devtools/network) on flutter.dev. + +## Hosted DevTools Inspector tab in IntelliJ + +For a while now, we’ve been maintaining two copies of some of our Flutter tools, like the **Inspector** pane in IntelliJ and the **Inspector** tab in Dart DevTools. Not only does this slow us down because we’ve got to maintain two codebases, but some features haven’t yet made it into the IntelliJ plugin, like the Layout Explorer. So, to solve both of those problems, we’ve enabled the ability to host the **Inspector** tab from Dart DevTools directly inside of IntelliJ. + +<DashImage figure src="images/0RQZ-EVyFP1-BqBIX.webp" /> + + +Notice the addition of the Layout Explorer, which you can use right next to your code. To toggle this option on, go to **Preferences** > **Languages & Frameworks** > **Flutter** > **Enable embedded DevTools inspector**. + +## Improved output linking in Visual Studio Code + +A regular activity that all Flutter developers face is going from the error output in the terminal or in their stack traces. In the most recent release of the Flutter extension for Visual Studio Code, these links are now properly parsed for you to enable links directly from the output. + +<DashImage figure src="images/0prigrwpp7gE6ChCE.webp" /> + + +It seems like a small thing, but initial feedback is already very positive on this feature. + +As always, there are too many tooling changes list here, but I recommend the following announcements for details: + +* [Dart DevTools — 0.9.0](https://groups.google.com/g/flutter-announce/c/UxMv8MzE_uo/m/ED539pi2AAAJ) + +* [Dart DevTools — 0.9.1](https://groups.google.com/g/flutter-announce/c/y27h86ATFJM) + +* [Dart DevTools — 0.9.3](https://groups.google.com/g/flutter-announce/c/24LppkXdMtM) + +* [Flutter IntelliJ Plugin M48.1 Release](https://groups.google.com/g/flutter-announce/c/nvgDi3RLAUE/m/Fx4Ze0vrBAAJ) + +* [Flutter IntelliJ Plugin M49 Release](https://groups.google.com/g/flutter-announce/c/-ZMKRIBRtGU) + +* [Flutter IntelliJ Plugin M50 Release](https://groups.google.com/g/flutter-announce/c/u0zU6zv3o44/m/2y0JsX1_AwAJ) + +* [VS Code extensions v3.14.0](https://groups.google.com/g/flutter-announce/c/8e8e-ZrgySY) + +* [VS Code extensions v3.15.0](https://dartcode.org/releases/v3-15/) + +## Customer Spotlight: EasyA + +EasyA is a subscription app designed to give school-age students access to brilliant tutors via instant messaging, and is written in Flutter. Recently it was featured by Apple as [their App of the Day](https://apps.apple.com/gb/story/id1527472788). + +<DashImage figure src="images/0yLWTxV_YTJ2HKhuz.webp" /> + +> *“When schools began to go online earlier this year, we knew we needed to launch our tutoring app quickly to help students. The sheer speed of development with Flutter meant we were able to implement award-winning designs for both iOS and Android, and also publish to the web — just in time for lockdown! Normally, this would have been practically impossible. But since Flutter allows us to target all three platforms at once, we were able to share code efficiently and leverage our small team of developers to the fullest.”* +> *— Phil Kwok, Co-founder, [EasyA](https://easya.io/)* + +## Breaking Changes + +As always, we attempt to keep the number of breaking changes to a minimum. Here’s the list from the Flutter 1.22 release. + +* [56413](https://github.com/flutter/flutter/pull/56413) [Prevent viewport.showOnScreen from scrolling the viewport if the specified Rect is already visible](https://docs.google.com/document/d/1BZhxy176uUnqOCnXdnHM1XetS9mw9WIyUAOE-dgVdUM/edit?usp=sharing). + +* [62395](https://github.com/flutter/flutter/pull/62395) [gen_l10n] Synthetic package generation by default + +* [62588](https://github.com/flutter/flutter/pull/62588) Build routes even less. + +## Summary + +The Flutter 1.22 stable release may have come quickly on the heels of the previous release, but there’s so much good stuff in it that this post couldn’t mention it all. We hope this release helps you build amazing apps for iOS and Android, and we can’t wait to see what you have in store! Thank you for your support — we build Flutter for you. \ No newline at end of file diff --git a/sites/www/content/blog/announcing-flutter-1-5/images/1WNHFzXBcxTZVLYKHj5Pu_w.webp b/sites/www/content/blog/announcing-flutter-1-5/images/1WNHFzXBcxTZVLYKHj5Pu_w.webp new file mode 100644 index 0000000000..15a5b2e09e Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-5/images/1WNHFzXBcxTZVLYKHj5Pu_w.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-5/images/1udBMeDS6NNhlgbXYyHA_iA.webp b/sites/www/content/blog/announcing-flutter-1-5/images/1udBMeDS6NNhlgbXYyHA_iA.webp new file mode 100644 index 0000000000..3bfc633f71 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-5/images/1udBMeDS6NNhlgbXYyHA_iA.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-5/index.md b/sites/www/content/blog/announcing-flutter-1-5/index.md new file mode 100644 index 0000000000..2f153f7b1b --- /dev/null +++ b/sites/www/content/blog/announcing-flutter-1-5/index.md @@ -0,0 +1,59 @@ +--- +title: "Announcing Flutter 1.5" +description: "With Google I/O 2019 just around the corner, we’re pleased to announce the availability of our new stable build, Flutter 1.5. With 336…" +publishDate: 2019-05-07 +author: csells +image: images/1udBMeDS6NNhlgbXYyHA_iA.webp +category: releases +layout: blog +--- + +With Google I/O 2019 just around the corner, we’re pleased to announce the availability of our new stable build, Flutter 1.5. With 336 contributors world-wide and more than 1,700 commits, this is our biggest release yet! With this release, we continue to focus on quality and stability, fixing the highest impact issues submitted by the community while continuing to follow [the Flutter 2019 Roadmap](https://github.com/flutter/flutter/wiki/Roadmap). + +<DashImage figure src="images/1udBMeDS6NNhlgbXYyHA_iA.webp" /> + + +## What’s new? + +[The Flutter 1.5.4 release](https://github.com/flutter/flutter/wiki/Release-Notes-Flutter-1.5.4) comes with a number of updates for the iOS and Material widgets, increased support for our experiments in web and desktop, a number of important updates to our plugins and tools as well as fixes to two regressions from Flutter 1.2. However, the most requested new feature in Flutter 1.5 is [the In-App Purchase plugin](https://pub.dartlang.org/packages/in_app_purchase), which is now available in beta for Android and iOS. + +<DashImage figure src="images/1WNHFzXBcxTZVLYKHj5Pu_w.webp" alt="In-App Purchase plugin beta in action" caption="In-App Purchase plugin beta in action" /> + + +The IAP plugin integrates with the underlying in-app purchase services provided for both Android and iOS, allowing your Flutter app to easily show in-app products that are available for sale, allow users to purchase products and show in-app products that the user currently owns. + +One final notable change is this release is support for a recent [update](https://developer.apple.com/news/?id=03202019a) to the Apple Store policy. This policy change requires all new iOS apps and iOS app updates to target the 12.1 version of the iOS SDK. While we’ve supported the iOS 12.1 SDK since its release, this release also builds all dependencies with the latest Apple SDK. To take advantage of this, we recommend that you use the Flutter 1.5 release or greater moving forward for all updates and new apps that you ship to the Apple store. + +## On the road to Google I/O + +This year’s Google I/O is going to have even more Flutter in it! For example, [the Flutter Create contest](https://flutter.dev/create) has closed and we had hundreds of entries! The judges did a lot of work to sort through all of the entries and pick the prize winners. We’ll showcase the best entries at I/O and announce the winner, including the Grand Prize winner who gets a fully-loaded Apple iMac Pro worth over $10,000. I can only assume they’ll use it to build more gorgeous Flutter apps even faster… + +If you’d like to participate in Flutter at I/O, you can watch the talks in person or via livestream: + +* [Beyond Mobile: Material Design, Adaptable UIs, and Flutter](https://events.google.com/io/schedule/events/1d9c02ed-bdc5-4a32-90bc-7316d6d1fb55) + +* [Beyond Mobile: Building Flutter Apps for iOS, Android, Chrome OS, and Web](https://events.google.com/io/schedule/events/03d8425c-54ca-437b-bac7-ece76cca8347) + +* [Pragmatic State Management in Flutter](https://events.google.com/io/schedule/events/0cedc311-b646-4b29-b952-d7c7a832bfbd) + +* [Dart: Multi-Platform, Productive, Fast — Pick 3](https://events.google.com/io/schedule/events/664c8a66-50d8-41d5-933c-0983f878b377) + +* [Building for iOS with Flutter](https://events.google.com/io/schedule/events/37261739-76c8-45fe-a8a8-5cd9b1a894c2) + +Should these talks inspire you to give Flutter a try, there are [lots of Codelabs](https://flutter.dev/docs/codelabs) that lead you through the experience, including several new ones being released at I/O! + +And finally, if you happen to be at I/O in person and you want to talk with the Flutter team directly, you can come to our Sandbox, join us in the Codelab space or visit with us at several sessions set aside specifically for that purpose: + +* [Flutter Office Hours or #AskFlutter](https://events.google.com/io/schedule/events/e1d97fc3-0077-4b88-a0ca-6811e2612766-bundle) (1) + +* [Flutter Office Hours or #AskFlutter](https://events.google.com/io/schedule/events/620c8218-1e23-481e-9bed-76d2ffd31c9c-bundle) (2) + +* [Flutter and Dart Team Meets the Community](https://events.google.com/io/schedule/events/00b99297-b183-4b05-aa94-dbaff635b8e1) + +* [Material Design Components: Web/iOS/Flutter Office Hours](https://events.google.com/io/schedule/events/b442b962-80af-48bc-b24d-2e15e5b87d96-bundle) (1) + +* [Material Design Components: Web/iOS/Flutter Office Hours](https://events.google.com/io/schedule/events/5184c64c-3051-449d-8779-1d1c9edb438e-bundle) (2) + +* [Material Design Components: Web/iOS/Flutter Office Hours](https://events.google.com/io/schedule/events/9d4bc7fa-2225-43e9-ae52-0f63dd8b00d8-bundle) (3) + +However you’re finding out about Flutter 1.5, the team hopes that you enjoy this release. What are you going to build? \ No newline at end of file diff --git a/sites/www/content/blog/announcing-flutter-1-7/images/00tdr7sHesqXfjWwK.webp b/sites/www/content/blog/announcing-flutter-1-7/images/00tdr7sHesqXfjWwK.webp new file mode 100644 index 0000000000..a4d0e7b4e4 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-7/images/00tdr7sHesqXfjWwK.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-7/images/1PzRLnOtNMtoO59dPPz--hA.webp b/sites/www/content/blog/announcing-flutter-1-7/images/1PzRLnOtNMtoO59dPPz--hA.webp new file mode 100644 index 0000000000..7ab71f3da4 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-7/images/1PzRLnOtNMtoO59dPPz--hA.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-7/images/1mz1byB5RNbLttBIDGINljg.jpeg b/sites/www/content/blog/announcing-flutter-1-7/images/1mz1byB5RNbLttBIDGINljg.jpeg new file mode 100644 index 0000000000..0672d65e83 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-7/images/1mz1byB5RNbLttBIDGINljg.jpeg differ diff --git a/sites/www/content/blog/announcing-flutter-1-7/images/1rARvCCVpn-cp1XoP5gXt9Q.webp b/sites/www/content/blog/announcing-flutter-1-7/images/1rARvCCVpn-cp1XoP5gXt9Q.webp new file mode 100644 index 0000000000..49a4defb9a Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-7/images/1rARvCCVpn-cp1XoP5gXt9Q.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-7/images/1rruCdwqtc2p3SXaMzUuE0Q.webp b/sites/www/content/blog/announcing-flutter-1-7/images/1rruCdwqtc2p3SXaMzUuE0Q.webp new file mode 100644 index 0000000000..7b39beac8b Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-1-7/images/1rruCdwqtc2p3SXaMzUuE0Q.webp differ diff --git a/sites/www/content/blog/announcing-flutter-1-7/index.md b/sites/www/content/blog/announcing-flutter-1-7/index.md new file mode 100644 index 0000000000..9f63a1ce16 --- /dev/null +++ b/sites/www/content/blog/announcing-flutter-1-7/index.md @@ -0,0 +1,93 @@ +--- +title: "Announcing Flutter 1.7" +description: "Continued refinement and polish for mobile platforms" +publishDate: 2019-07-09 +author: timsneath +image: images/1PzRLnOtNMtoO59dPPz--hA.webp +category: releases +layout: blog +--- + +Today we’re pleased to **announce the general availability of Flutter 1.7**, a smaller release after the major feature announcements at Google I/O. Flutter 1.7 contains support for AndroidX and for updated Play Store requirements, a number of new and enhanced components, and bug fixes to customer-reported issues. + +<DashImage figure src="images/1PzRLnOtNMtoO59dPPz--hA.webp" /> + + +If you already have Flutter on your system and you’re on the default stable channel, you can upgrade to version 1.7 by running `flutter upgrade` from the command line. The updated release is also included in [a new installation](https://flutter.dev/docs/get-started/install) of Flutter. + +## AndroidX Support for New Apps + +[AndroidX](https://developer.android.com/jetpack/androidx) is a new open source support library from the Jetpack team that helps Android apps stay updated with the latest components without sacrificing backward compatibility. Now that AndroidX is itself stable and many Flutter packages have been updated to support it, Flutter supports [creating new Flutter projects with AndroidX](https://github.com/flutter/flutter/pull/31028), which reduces the work needed to integrate with other parts of the Android ecosystem. + +When creating a Flutter project, you can add the `--androidx` flag to ensure the generated project targets the new support library. Information about migrating existing projects to AndroidX can be found [on flutter.dev](https://flutter.dev/docs/development/packages-and-plugins/androidx-compatibility#for-plugin-maintainers-migrating-a-flutter-plugin-to-androidx). We’re actively working on bringing AndroidX / Jetifier support for apps with mixed AndroidX / Android Support libraries, such as in add-to-app cases, and will have more to share on this front in a forthcoming post. + +## Support for Android app bundles and 64-bit Android apps + +From August 1st, 2019, Android apps that use native code and target Android 9 Pie will be [required to provide a 64-bit version](https://developer.android.com/distribute/best-practices/develop/64-bit) in addition to the 32-bit version when publishing to the Google Play Store. While Flutter has long supported generating 64-bit Android apps, version 1.7 adds support for creating [Android App Bundles](https://developer.android.com/guide/app-bundle) that target both 64-bit and 32-bit from a single submission. See the updated [documentation on publishing Flutter-based Android apps](https://flutter.dev/docs/deployment/android) to learn how to do this, as well as how to create separate APK files for both 32-bit and 64-bit devices. + +## New widgets and framework enhancements + +We want your apps to look great and feel natural, regardless of what platform you’re targeting. Correspondingly, we continue to update and enhance the widgets available for both Android and iOS. + +This release features a new [`RangeSlider`](https://github.com/flutter/flutter/pull/31681) control that lets you select a range of values on a single slider (for example a minimum and maximum temperature value): + +<DashImage figure src="images/1rruCdwqtc2p3SXaMzUuE0Q.webp" alt="The new, themeable RangeSlider widget supports continuous or discrete styles" caption="The new, themeable RangeSlider widget supports continuous or discrete styles" /> + + +The [updated `SnackBar` widget](https://github.com/flutter/flutter/pull/31275) supports an updated look in the Material spec, and a [number](https://github.com/flutter/flutter/pull/31294) [of](https://github.com/flutter/flutter/pull/32177) [new](https://github.com/flutter/flutter/pull/31929) [samples](https://github.com/flutter/flutter/pull/32703) are added [to the](https://github.com/flutter/flutter/pull/34679) [documentation](https://github.com/flutter/flutter/pull/32530). + +For [Cupertino](https://flutter.dev/docs/development/ui/widgets/cupertino), the Flutter library for building pixel-perfect iOS applications, we’ve made a number of updates. In particular, we’ve improved the fidelity of the [`CupertinoPicker` and `CupertinoDateTimePicker` widgets](https://github.com/flutter/flutter/pull/31464), and added support for localization to non-English languages. + +We also made major improvements to the [text selection and editing experience on iOS](https://flutter.dev/docs/resources/platform-adaptations#text-editing), regardless of whether you’re using the Material or Cupertino design language. Also, a [new sample](https://github.com/flutter/samples/tree/master/platform_design) demonstrates how to make more significant platform adaptations across iOS and Android while retaining the same codebase. + +Text rendering gets a big upgrade with support for rich [typography features](https://api.flutter.dev/flutter/painting/TextStyle/fontFeatures.html), including tabular and old-style numbers, slashed zeros, and stylistic sets, as [this demo](https://github.com/timsneath/typography) shows: + +<DashImage figure src="images/00tdr7sHesqXfjWwK.webp" alt="With Flutter, you can now add sophisticated typography with OpenType font feature support" caption="With Flutter, you can now add sophisticated typography with OpenType font feature support" /> + + +Lastly, we’ve added support for [game controllers](https://github.com/flutter/flutter/pull/33868). Could this lead to some fun Flutter apps? You tell us! + +## Focus on the Fundamentals + +Flutter 1.7 represents a lot of hard work by the team to respond to customer-reported issues, with [over 1,250 issues closed in the two months](https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aclosed+closed%3A2019-04-22..2019-06-21+sort%3Areactions-%2B1-desc) since our last stable release. + +With the rapid growth in Flutter, we’re seeing lots of new issues reported, and to be transparent, the bug process that worked well when our project was smaller is not working so well now. As a result, our open issue count has increased significantly over the last few months, despite our progress in closing triaged issues. We’re working to increase staffing in this area, which will help with faster triaging of new bugs, closing and merging duplicate issues and redirecting support requests to [StackOverflow](https://stackoverflow.com/questions/tagged/flutter). + +In recent surveys, many of you said that you’d like to see us continue to invest in documentation and error messages. One key part of that work is to provide better structure for our errors which tools like VSCode and Android Studio can take advantage of in the future. You can see examples of this work [in issue 34684](https://github.com/flutter/flutter/pull/34684). + +We also fixed the top crashing bug, which was an error when the Flutter tool is unable to write to the Flutter directory. Flutter now fails gracefully if the user doesn’t have write permissions, with clearer indications on how to fix the problem. + +In terms of documentation, we have an ever increasing list of samples that can be created directly from the flutter create tool. From the command line, you can run a command such as: + +``` +flutter create --sample=material.AppBar.1 mysample +``` + + +If a sample can be created in this way, you’ll see a “Sample in the App” tab in the documentation, as in [this example for the AppBar widget](https://master-api.flutter.dev/flutter/material/AppBar-class.html): + +<DashImage figure src="images/1rARvCCVpn-cp1XoP5gXt9Q.webp" /> + + +We’re also continuing to embed the popular [Widget of the Week](https://www.youtube.com/playlist?list=PLjxrf2q8roU23XGwz3Km7sQZFTdB996iG) videos directly into the documentation, as an easy way to grok the various widgets in Flutter’s toolkit. + +Behind the scenes, you’ll see lots of underlying work to create infrastructure towards enabling Flutter on macOS and Windows, with further support for important concepts like right-click and unique platform infrastructure such as [MSBuild](https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild?view=vs-2019). Support for non-mobile platforms is not yet available in the stable channel, however. + +Lastly, when you’re building Flutter apps on the Mac, we now have support for [the new Xcode build system](https://github.com/flutter/flutter/pull/33684). This is on by default for new projects, and [easy to enable for existing projects](https://github.com/flutter/flutter/issues/20685#issuecomment-509731873). + +## An ever-growing Flutter community + +As ever, it’s exciting to see Flutter continue to grow in popularity and usage, and we also celebrate the ways customers large and small are using Flutter. Since I/O, the team has been busy with various events around the world: from [GMTC](https://gmtc2019.geekbang.org/) in China to meetups and presentations in New York and Mexico; it’s been great to meet with many of you and hear about some of the apps that you’re building. + +We’ve talked about [Reflectly](https://www.forbes.com/sites/heatherfarmbrough/2018/05/01/reflectly-wants-to-be-an-adidas-of-the-mind/#572291294204) before: a small Danish company who built a beautiful mindfulness app for iOS and Android. Their app was just featured as Apple’s App of the Day on their US iPhone app store, demonstrating how Flutter apps are more than capable of delivering reference-quality experiences: + +<YoutubeEmbed id="6ZpETbJjipQ" title="Reflectly App (Flutter Developer Story)" fullwidth="true"/> + + +And at the [WeAreDevelopers](https://events.wearedevelopers.com/) conference in Berlin, [BMW announced their new Flutter-based app](https://youtu.be/80pRyn7fZRk?t=1234), currently in development. Here’s what Guy Duncan, CTO Connected Company at BMW, had to say: +> *“By combining Dart and Flutter we have the first true cross-platform mobile toolkit; we feel it is a game changer to ensure feature parity for digital touchpoints and IoT.* +> *By moving forward with world class tooling, automation and modern functional programming patterns we can improve feature cycle time, security, and cost of delivery of features for the business.”* + +Beyond apps, of course the open source community is what makes Flutter such a fun place to work, with so many [resources](https://flutterx.com/), [plugins](https://pub.dev/flutter), [events](https://flutterevents.com/) and [meetups](https://www.meetup.com/topics/flutter/). We continue to be amazed by how you’re using Flutter and are honored to be able to share the fun with you all! + +<DashImage figure src="images/1mz1byB5RNbLttBIDGINljg.jpeg" alt="Photo credit: [@damian2048](https://twitter.com/damian2048)" caption="Photo credit: [@damian2048](https://twitter.com/damian2048)" /> diff --git a/sites/www/content/blog/announcing-flutter-2-2-at-google-i-o-2021/images/0NCBwYFewFf602uvF.webp b/sites/www/content/blog/announcing-flutter-2-2-at-google-i-o-2021/images/0NCBwYFewFf602uvF.webp new file mode 100644 index 0000000000..f96ba1e978 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-2-2-at-google-i-o-2021/images/0NCBwYFewFf602uvF.webp differ diff --git a/sites/www/content/blog/announcing-flutter-2-2-at-google-i-o-2021/images/0ZdwECz0chT1hOq6Y.webp b/sites/www/content/blog/announcing-flutter-2-2-at-google-i-o-2021/images/0ZdwECz0chT1hOq6Y.webp new file mode 100644 index 0000000000..8d5e47f530 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-2-2-at-google-i-o-2021/images/0ZdwECz0chT1hOq6Y.webp differ diff --git a/sites/www/content/blog/announcing-flutter-2-2-at-google-i-o-2021/images/1zroRqBB-vjUR_UCvYCKSUg.webp b/sites/www/content/blog/announcing-flutter-2-2-at-google-i-o-2021/images/1zroRqBB-vjUR_UCvYCKSUg.webp new file mode 100644 index 0000000000..96f6a0d795 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-2-2-at-google-i-o-2021/images/1zroRqBB-vjUR_UCvYCKSUg.webp differ diff --git a/sites/www/content/blog/announcing-flutter-2-2-at-google-i-o-2021/index.md b/sites/www/content/blog/announcing-flutter-2-2-at-google-i-o-2021/index.md new file mode 100644 index 0000000000..30403ad3c3 --- /dev/null +++ b/sites/www/content/blog/announcing-flutter-2-2-at-google-i-o-2021/index.md @@ -0,0 +1,65 @@ +--- +title: "Announcing Flutter 2.2 at Google I/O 2021" +description: "Growing momentum for the leading UI toolkit for multiplatform development" +publishDate: 2021-05-18 +author: timsneath +image: images/0NCBwYFewFf602uvF.webp +category: events +layout: blog +--- + +At Google I/O today, we announced [Flutter 2.2](https://flutter.dev/docs/whats-new), our latest release of the open source toolkit for building beautiful apps for any device from a single platform. Flutter 2.2 is the best version of Flutter yet, with updates that make it easier than ever for developers to monetize their apps through in-app purchases, payments and ads; to connect to cloud services and APIs that extend apps to support new capabilities; and with tooling and language features that allow developers to eliminate a whole class of errors, increase app performance and reduce package size. + +<DashImage figure src="images/0NCBwYFewFf602uvF.webp" /> + + +## Building on the foundation of Flutter 2 + +Flutter 2.2 is built on the foundation of Flutter 2, which extended Flutter from its mobile roots to incorporate web, desktop, and embedded usage. It is uniquely designed for a world of ambient computing, where users have a wide variety of different devices and form factors and are looking for consistent experiences that span across their daily lives. With Flutter 2.2, enterprises, startups, and entrepreneurs alike can build high-quality solutions that can reach the full potential of their addressable market, allowing creative inspiration (rather than target platform) to be the only limiting factor. +> # Flutter is now the most popular framework for cross-platform development. + +A recent mobile developer study highlights the growth of Flutter. Analyst firm [SlashData](https://www.slashdata.co/)’s [Mobile Developer Population Forecast 2021](https://www.slashdata.co/reports/?category=mobile-desktop) shows that Flutter is now the most popular framework for cross-platform development, with 45% of developers selecting it, representing 47% growth between Q1 2020 and Q1 2021. Our own data confirms this shift towards Flutter; in the last 30 days, more than one in eight of the new apps in the Play Store are built with Flutter. + +At I/O, we shared that there are now over 200,000 apps in the Play Store alone built using Flutter. These apps come from companies like Tencent, whose [WeChat](https://apps.apple.com/us/app/wechat/id414478124) messaging app is used by over 1.2 billion users on iOS and Android; [ByteDance](https://www.bytedance.com/en/products/), originators of TikTok, who have now built 70 distinct apps using Flutter; and other apps from companies including [BMW](https://www.press.bmwgroup.com/global/article/detail/T0328610EN/the-my-bmw-app:-new-features-and-tech-insights-for-march-2021?language=en), [SHEIN](https://apps.apple.com/app/id878577184), [Grab](https://apps.apple.com/app/id647268330) and [DiDi](https://play.google.com/store/apps/details?id=com.xiaojukeji.didi.global.customer&hl=None). Of course, Flutter isn’t just used by large corporations. Some of the most innovative apps are coming from names you might not have heard of: for example, [Wombo](https://play.google.com/store/apps/details?id=com.womboai.wombo&hl=None), the viral singing selfie app; [Fastic](https://play.google.com/store/apps/details?id=de.fastic.app&hl=None), the intermittent fasting app, and [Kite](https://play.google.com/store/apps/details?id=com.zerodha.kite3&hl=None), a beautiful investment trading app. + +## Introducing Flutter 2.2 + +The Flutter 2.2 release is focused on improvements to the development experience to help you deliver more reliable, performant apps to your customers. + +Sound null safety is now the default for new projects. Null safety adds protection against null reference exceptions, giving developers the means to express non-nullable types in their code. And since Dart’s implementation is *sound*, the compiler can eliminate null checks at runtime, providing increased performance for your apps. The ecosystem has responded quickly, with around 5,000 packages already updated to support null safety. + +There are lots of performance improvements in this release also: for web apps, we offer background caching using service workers; for Android apps, Flutter supports deferred components; for iOS, we’ve been working on tooling to precompile shaders to eliminate or reduce first-run jank. We’ve also added a number of new features to the DevTools suite that help you understand how memory is allocated in your apps, as well as support for third-party tools extensions. + +Additionally, we’ve been working on a few important areas of polish, such as improved accessibility for web targets. + +Our work extends beyond the core of Flutter. We’ve also been partnering with other Google teams to help integrate Flutter into our broader developer stack. In particular, we continue to build trusted services that help developers responsibly monetize their apps. Our [new ads SDK](https://developers.google.com/admob/flutter/quick-start) is updated in this release with null safety and support for adaptive banner formats. We’re also introducing a[ new payment plugin](http://pub.dev/packages/pay), built in partnership with the Google Pay team, that lets you take payment for physical goods on both iOS and Android. And we have updated our [in-app purchases plugin](https://pub.dev/packages/in_app_purchase), along with a matching [codelab](https://codelabs.developers.google.com/codelabs/flutter-in-app-purchases). + +As the “secret sauce” that powers Flutter, [Dart](https://dart.dev) also gets an update in this release. Dart 2.13 expands support for native interoperability, with support for arrays and packed structs in FFI. It also includes support for type aliases, which increase readability and provide a gentle pathway for certain refactoring scenarios. We continue to add integrations for the broader ecosystem, with a Dart [GitHub Action](https://github.com/marketplace/actions/setup-dart-sdk) and a curated [Docker Official Image](https://hub.docker.com/_/dart) that is optimized for cloud-based deployment of business logic. + +## More than a Google project + +While Google continues to be the primary contributor to the Flutter project, we’re delighted to see the growth of the broader ecosystem around Flutter. + +<DashImage figure src="images/1zroRqBB-vjUR_UCvYCKSUg.webp" /> + + +One area of particular growth over recent months has been the broadening of Flutter to an ever growing number of platforms and operating systems. At Flutter Engage, we announced that [Toyota is bringing Flutter to their next generation vehicle infotainment systems](https://medium.com/googleplaydev/seamless-multi-platform-app-development-with-flutter-ea0e8003b0f9#f53d). And last month, Canonical shipped their first release of [Ubuntu with integrated support for Flutter](https://ubuntu.com/blog/ubuntu-21-04-is-here), with Snap integration and support for Wayland. + +Two new partners demonstrate this ever-growing ecosystem. [Samsung is porting Flutter to Tizen](https://github.com/flutter-tizen/flutter-tizen), with an open source repository that others can also contribute to. And [Sony is leading the effort to deliver a solution for embedded Linux](https://github.com/sony/flutter-embedded-linux). + +Designers benefit also from the open source nature of this project, with the announcement from [Adobe of its updated XD to Flutter plugin](https://medium.com/adobetech/announcing-xd-to-flutter-v2-0-82d09f3909a7). Adobe XD provides designers a great way to experiment and iterate, and now with enhanced Flutter support, designers and developers can collaborate on the same assets, putting great ideas into production faster than ever. + +Lastly, Microsoft continues to collaborate with us; besides the work the Surface team has been doing to build foldable experiences with Flutter, this week sees the [alpha of Flutter support for UWP apps](https://flutter.dev/desktop#windows-uwp) built for Windows 10. We’re excited to see more apps that take advantage of the platform adaptations built into Flutter to provide a great experience across mobile, desktop, web and beyond. + +## Building Great Experiences + +More than anything, we built Flutter to help developers build great experiences. We are animated by the idea that app development can be better: that we can empower you by removing traditional impediments to reaching your audience. + +We love seeing how you put Flutter to work. One example comes in the form of a project from the US Veterans Administration. The video below shows how their Flutter app is helping them provide rehabilitation for soldiers with post-traumatic stress disorders. + +<YoutubeEmbed id="2S-KkvFuLWs" title="STAIR (Flutter Developer Stories)" fullwidth="true"/> + + +With a [wide variety of workshops, presentations and on-demand sessions about Flutter](https://events.google.com/io/program/content?4=topic_flutter) at Google I/O, we’re excited to share our work with all of you. And don’t forget to check out our fun [photo booth web app](https://photobooth.flutter.dev), built with Flutter, which lets you create a selfie with our Dash mascot and her friends! + +<DashImage figure src="images/0ZdwECz0chT1hOq6Y.webp" /> diff --git a/sites/www/content/blog/announcing-flutter-2-8/images/0_hVZ_qoWPxzQK3ws.webp b/sites/www/content/blog/announcing-flutter-2-8/images/0_hVZ_qoWPxzQK3ws.webp new file mode 100644 index 0000000000..27f1e32ba7 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-2-8/images/0_hVZ_qoWPxzQK3ws.webp differ diff --git a/sites/www/content/blog/announcing-flutter-2-8/images/1I_1j9GuOmp3HY8ODOkXdbQ.webp b/sites/www/content/blog/announcing-flutter-2-8/images/1I_1j9GuOmp3HY8ODOkXdbQ.webp new file mode 100644 index 0000000000..20472de3da Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-2-8/images/1I_1j9GuOmp3HY8ODOkXdbQ.webp differ diff --git a/sites/www/content/blog/announcing-flutter-2-8/images/1U69So9lK-pKoIngVnhbAaw.webp b/sites/www/content/blog/announcing-flutter-2-8/images/1U69So9lK-pKoIngVnhbAaw.webp new file mode 100644 index 0000000000..f0e5966eaf Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-2-8/images/1U69So9lK-pKoIngVnhbAaw.webp differ diff --git a/sites/www/content/blog/announcing-flutter-2-8/images/1YeJGwc8CcHF7v2rg1-inMA.webp b/sites/www/content/blog/announcing-flutter-2-8/images/1YeJGwc8CcHF7v2rg1-inMA.webp new file mode 100644 index 0000000000..940aca60f2 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-2-8/images/1YeJGwc8CcHF7v2rg1-inMA.webp differ diff --git a/sites/www/content/blog/announcing-flutter-2-8/images/1cHEQ6vW829J09wMaOTCiNA.webp b/sites/www/content/blog/announcing-flutter-2-8/images/1cHEQ6vW829J09wMaOTCiNA.webp new file mode 100644 index 0000000000..311861cfb2 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-2-8/images/1cHEQ6vW829J09wMaOTCiNA.webp differ diff --git a/sites/www/content/blog/announcing-flutter-2-8/images/1h25t5nbufPWhips6raHknQ.webp b/sites/www/content/blog/announcing-flutter-2-8/images/1h25t5nbufPWhips6raHknQ.webp new file mode 100644 index 0000000000..2d1a82264a Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-2-8/images/1h25t5nbufPWhips6raHknQ.webp differ diff --git a/sites/www/content/blog/announcing-flutter-2-8/index.md b/sites/www/content/blog/announcing-flutter-2-8/index.md new file mode 100644 index 0000000000..d92e40605c --- /dev/null +++ b/sites/www/content/blog/announcing-flutter-2-8/index.md @@ -0,0 +1,73 @@ +--- +title: "Announcing Flutter 2.8" +description: "A new release of Flutter: and a look back on a year of growth" +publishDate: 2021-12-08 +author: timsneath +image: images/0_hVZ_qoWPxzQK3ws.webp +category: releases +layout: blog +--- + +Just in time for the holidays, today we’re announcing a big update to Flutter, with a number of new features and improvements that refine the experience for mobile and web developers and bring us closer to stable support for desktop. + +<DashImage figure src="images/0_hVZ_qoWPxzQK3ws.webp" /> + + +Flutter aims to change how apps are built. It unites mobile, web, desktop, and embedded development into a single toolkit: letting developers focus first on what they want to build, rather than which platforms they want to target; offering a high-performance, high-productivity framework that shortens the inner loop for developers; and enabling one codebase to target multiple platforms and form factors. + +## New features and improvements: faster and more productive + +One of the major areas of focus for this release is **mobile performance**. Ideally great performance would come for free, but in practice any complex app needs optimization to make sure it uses the underlying hardware and libraries well. That includes startup performance, which can be constrained by network bandwidth or other initialization costs; memory usage, particularly on memory-constrained devices; and graphics rendering. We’ve been using some of our experiences with large Google apps like [Google Pay](https://flutter.dev/showcase/google-pay) to invest both in making Flutter itself more performant, and in giving you better tooling to guide profiling and optimization of your own app. Your apps should start faster and use less memory just by upgrading to Flutter 2.8. + +The latest update makes it easier than ever to **connect apps to back-end services**, such as Firebase and Google Cloud. It adds [production-quality support for Google Ads](https://flutter.dev/monetization) and major upgrades to the camera and embedded web plugins. The release also includes [Dart 2.15](https://medium.com/dartlang/dart-2-15-7e7a598e508a), which adds major improvements to concurrency, new language features like constructor tearoffs and enhanced enumerations, and optimizations that deliver a 10% reduction in memory utilization. + +<DashImage figure src="images/1YeJGwc8CcHF7v2rg1-inMA.webp" alt="With the new developer productivity enhancements to Flutter, an app can support sign-in to multiple authentication services with just a single drop-in widget." caption="With the new developer productivity enhancements to Flutter, an app can support sign-in to multiple authentication services with just a single drop-in widget." /> + + +Another big theme of investment in this release that you’ll see us carry forward into future releases is further **improving developer productivity**. With features like stateful hot reload, we’ve always focused on creating a tight inner loop for developers. We’re now starting to explore some higher-level abstractions that make it easier for developers to get running faster. For example, in this release we’re adding a sign-in widget that uses Firebase to handle authentication. With this widget, you don’t have to worry about all the edge cases of sign-in, such as two-factor authentication or reset password user flows, nor about the complexities of supporting Google, Apple, Twitter, or Facebook as an auth provider. Features like this, building on the core foundations of Flutter, have the potential to transform how developers build apps, combining the development speed of low-code solutions with the flexibility and power of a full UI framework. + +Both Flutter 2.8 and Dart 2.15 are available now, and should be an easy upgrade for existing apps running the previous version. For greater detail, we have a technical blog post covering the enhancements in each of [Dart](https://medium.com/dartlang/dart-2-15-7e7a598e508a) and [Flutter](https://medium.com/flutter/whats-new-in-flutter-2-8-d085b763d181). + +## Casual game development with Flame + +For most developers, Flutter is an app framework. But there’s also a growing ecosystem around casual game development, taking advantage of the hardware-accelerated graphics support provided by Flutter. + +Today, we are thrilled to celebrate the [1.0 release of Flame](https://flame-engine.org/), a modular 2D game engine built on top of Flutter. Flame provides what you need to build games quickly: as well as a game loop, it also includes core primitives such as a component system, animated sprites and images, collision detection, a world camera, an effects system, and gesture and input support. + +Flame is modular and can also be extended with packages that offer integrations to other libraries, for instance Rive (for animations), audioplayers (for music and sound effects), Forge2D (a Box2D-style physics engine), Tiled (tile maps editor), Fire Atlas (a sprite sheet and animation editor). Together, Flame and the broader ecosystem offer a strong set of services for a casual or 2D game developer. + +<DashImage figure src="images/1cHEQ6vW829J09wMaOTCiNA.webp" /> + + +<DashImage figure src="images/1I_1j9GuOmp3HY8ODOkXdbQ.webp" /> + + +<DashImage figure src="images/1h25t5nbufPWhips6raHknQ.webp" alt="*[Tomb Toad](https://play.google.com/store/apps/details?id=com.crescentmoongames.tombtoad), [Gravity Runner](https://play.google.com/store/apps/details?id=xyz.fireslime.gravitational_waves) and [Bonfire](https://bonfire-engine.github.io/examples/bonfire/#/): three examples of games built with Flame.*" caption="*[Tomb Toad](https://play.google.com/store/apps/details?id=com.crescentmoongames.tombtoad), [Gravity Runner](https://play.google.com/store/apps/details?id=xyz.fireslime.gravitational_waves) and [Bonfire](https://bonfire-engine.github.io/examples/bonfire/#/): three examples of games built with Flame.*" /> + + +Flame is created by [Blue Fire](https://dev.to/blue-fire/fireslime-is-now-blue-fire-405g), a group of contributors focusing on creating open source packages and plugins for Flutter and Dart. We’re delighted to partner with them and encourage you to check out Flame if you’re interested in game development. + +## Flutter’s continued momentum + +We’re amazed to see how fast Flutter continues to grow, with a flourishing ecosystem of apps and tools that build on top of the core framework. At this year’s Google I/O event, [we noted](https://medium.com/flutter/announcing-flutter-2-2-at-google-i-o-2021-92f0fcbd7ef9) that there were already over 200,000 apps built with Flutter in the Play Store. In just over six months since that event, **the number of Flutter apps has nearly doubled**, **with more than 375,000 Flutter apps now in the Play Store**. + +<DashImage figure src="images/1U69So9lK-pKoIngVnhbAaw.webp" alt="Flutter supports Android, iOS, iPadOS, web, Windows, macOS and Linux: so you don’t have to rewrite your app just to target a different device or form factor." caption="Flutter supports Android, iOS, iPadOS, web, Windows, macOS and Linux: so you don’t have to rewrite your app just to target a different device or form factor." /> + + +Flutter isn’t just used on Android, of course. According to independent mobile analyst firm [AppAnnie](https://www.appannie.com/en/), apps using Flutter on iOS include top brands and apps including [BMW](https://itunes.apple.com/app/id1519457734), [eBay](https://itunes.apple.com/app/id1456156090), [WeChat](https://apps.apple.com/us/app/wechat/id414478124), [SHEIN](https://apps.apple.com/us/app/shein-online-fashion/id878577184), [Philips Hue](https://apps.apple.com/app/id1055281310), [Norton](https://apps.apple.com/app/id1278474169), [trip.com](https://apps.apple.com/app/id681752345) and [Greggs](https://apps.apple.com/gb/app/greggs/id1098233626). On the web, Flutter is finding a home for app experiences, benefitting design tools like [FlutterFlow](https://flutterflow.io/) and [Rive](https://rive.app/). On desktop, the Ubuntu engineering team continues to build a variety of [new experiences with Flutter](https://github.com/canonical?q=&type=&language=dart&sort=), including a new installer and firmware updater. Even games like [PUBG](https://apps.apple.com/us/app/pubg-mobile-arcane/id1330123889) are finding that Flutter is a great fit for UI screens. + +Ecosystems take a long time to build, but Flutter is now the most popular multi-platform toolkit, as measured independently by [Statista](https://www.statista.com/statistics/869224/worldwide-software-developer-working-hours/), [JetBrains](https://www.jetbrains.com/lp/devecosystem-2021/miscellaneous/#Technology_which-cross-platform-mobile-frameworks-do-you-use-two-years), [SlashData](https://www.slashdata.co/reports/?category=mobile-desktop), and [Stack Overflow](https://insights.stackoverflow.com/trends?tags=flutter%2Creact-native%2Ccordova%2Cxamarin). We don’t take that for granted, but the growth of Flutter’s popularity leads to an ever broader ecosystem of [packages](https://pub.dev) and tools that support it. + +## Looking back at 2021, looking forward to 2022 + +Over the course of this difficult last year, our own engineering teams have been busy. As well as the features in Flutter 2.8, we’ve rewritten our developer tools, shipped null-safety and web support, completed FFI for native code integration, added initial [Material You support](https://material.io/blog/announcing-material-you), and worked hard to improve performance and quality. We’ve closed out almost 20,000 issues in total. We’ve created [a smart new website](https://flutter.dev) to better showcase Flutter. And we’ve spent a considerable amount of effort over the last few months overhauling our engineering infrastructure to increase engineer productivity and expand testing. + +As we look ahead to 2022, we hope to be able to get out and see some of you in person. We’re expecting to invest more in the core developer experience, including language enhancements, documentation updates, and higher-level abstractions that make it easier to build sophisticated apps with Flutter. We’re going to complete our stable desktop support and further expand our work on the web. And we’re planning to expand interoperability with other platforms and portability to new targets. We’re still only getting started! + +## A few closing thoughts and a dedication + +We want to dedicate this Flutter 2.8 release to Kevin Gray, [a developer at Very Good Ventures who sadly passed away just a week ago.](https://verygood.ventures/blog/remembering-our-friend-and-teammate-kevin-gray) Kevin has been a key contributor to the success of Flutter since the earliest days; he was the developer behind many of the early Flutter demos, including one of our first high-profile customer wins, the first ever demo of Flutter on desktop, and the first Flutter demo featured at a Google I/O keynote. He was a talented, caring, funny, and kind man and those who knew him will readily agree that he leaves a gaping hole. As we grieve, we also celebrate his life, and we remember him publicly so that his impact may be known by all. Flutter wouldn’t be what it is without him. + +Kevin, we miss you already. Thank you for all you did for Flutter, and for your friendship. To his family and friends, we send you our love. + +To our broader community of Flutter developers and supporters, we’re grateful for you. We hope that Flutter 2.8 gives you plenty to explore. And if you haven’t yet learned Flutter, join the nearly 40,000 developers who are currently working their way through the [Flutter Apprentice book](https://store.raywenderlich.com/products/flutter-apprentice-google), which is available for free to everyone until early January. We wish you a happy and safe holiday season. \ No newline at end of file diff --git a/sites/www/content/blog/announcing-flutter-3-24-and-dart-3-5/images/0QC1D0LdTgLynDOnV.webp b/sites/www/content/blog/announcing-flutter-3-24-and-dart-3-5/images/0QC1D0LdTgLynDOnV.webp new file mode 100644 index 0000000000..3a21b3603f Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-3-24-and-dart-3-5/images/0QC1D0LdTgLynDOnV.webp differ diff --git a/sites/www/content/blog/announcing-flutter-3-24-and-dart-3-5/images/1jzRGig761LnPlvokq2FaVA.gif b/sites/www/content/blog/announcing-flutter-3-24-and-dart-3-5/images/1jzRGig761LnPlvokq2FaVA.gif new file mode 100644 index 0000000000..9044f30e32 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-3-24-and-dart-3-5/images/1jzRGig761LnPlvokq2FaVA.gif differ diff --git a/sites/www/content/blog/announcing-flutter-3-24-and-dart-3-5/index.md b/sites/www/content/blog/announcing-flutter-3-24-and-dart-3-5/index.md new file mode 100644 index 0000000000..7894083bcd --- /dev/null +++ b/sites/www/content/blog/announcing-flutter-3-24-and-dart-3-5/index.md @@ -0,0 +1,73 @@ +--- +title: "Announcing Flutter 3.24 and Dart 3.5" +description: "An early look at Flutter GPU, web enhancements, and more!" +publishDate: 2024-08-06 +author: mit-mit +image: images/1jzRGig761LnPlvokq2FaVA.gif +category: releases +layout: blog +--- + +Today, we’re unveiling [Flutter 3.24](https://medium.com/p/6c040f87d1e4/edit) and [Dart 3.5](https://medium.com/dartlang/dart-3.5-6ca36259fa2f) alongside the last stop[ in the I/O 2024 Connect series,](https://ioconnectchina.googlecnapps.cn/) happening in just a few hours in China — one of Flutter’s most prolific communities in the world, making this moment very special. + +<DashImage figure src="images/1jzRGig761LnPlvokq2FaVA.gif" /> + + +We kicked off [Google I/O](https://io.google/2024/) in May with an [exciting slew of updates](https://medium.com/flutter/io24-5e211f708a37), including the graduation of support for WebAssembly compilation to the stable channel, improvements to Impeller, and an early look at the future of Dart macros. + +The Flutter 3.24 and Dart 3.5 releases build upon our mission to help you craft stunning, performant apps that reach users across mobile, web, and desktop — all from a single, shared codebase. They include an early preview of the new Flutter GPU API, enhancements to element embedding on the web, and several exciting updates for those interested in building for the iOS ecosystem, including early support for Swift Package Manager, and updated functionality to Cupertino widgets. + +Let’s jump in! + +## Impeller: Raising the bar for multi-platform graphics performance + +Historically, cross-platform frameworks have required compromise on visuals, due to a reliance on high-level abstractions offered by the underlying platforms. Flutter has taken a different approach, with its own rendering layer that offers hardware-accelerated graphics and smooth performance on every device. We’ve made substantial progress with [Impeller](https://docs.flutter.dev/perf/impeller) and [shaders](https://docs.flutter.dev/ui/design/graphics/fragment-shaders), unlocking exciting new possibilities in graphics — like 3D. + +We’re excited to share an early preview of the new [Flutter GPU API](https://github.com/flutter/engine/blob/main/docs/impeller/Flutter-GPU.md), a powerful, low-level graphics API integrated directly into the Flutter SDK. The API allows you to define custom raster pipelines and submit draw calls directly to the GPU, making it possible to create specialized renderers like 2D Canvas alternatives, 3D scene graphs, or even particle systems for visually stunning, performant, and immersive experiences without the engine-level bulk typically needed. + +<DashImage figure src="images/0QC1D0LdTgLynDOnV.webp" alt="3D animation of a sci-fi space helmet rendered in flutter_scene." caption="3D animation of a sci-fi space helmet rendered in flutter_scene." /> + + +Given how low-level the API is, we expect a learning curve for developers who don’t have significant experience developing graphics. That’s why we’re investing in rendering packages, like the new `flutter_scene` package, which leverages the Flutter GPU API to allow importing animated glTF models and constructing 3D scenes, enabling you to easily build 3D apps and games in Flutter and Dart, like the one below. + +<YoutubeEmbed id="Y-DFVKPikVM" title="3D scene demo" fullwidth="true"/> + + +While the Flutter GPU API offers exciting possibilities, it’s still in early preview and we might make breaking changes to the API. We recommend developing against Flutter’s main channel when using Flutter GPU. Learn more in the blog post [Introducing Flutter GPU & Flutter Scene](https://medium.com/flutter/getting-started-with-flutter-gpu-f33d497b7c11). + +## Flutter for iOS and macOS: making it easier to deliver beautiful, fast apps for the Apple ecosystem + +It’s our goal to empower you to build exceptional apps that feel native and perform flawlessly. Part of that work is optimizing performance as well as maximizing Flutter’s compatibility with the underlying platforms, including accessing the full power of the Apple ecosystem. + +In this release, we’ve introduced early support for Swift Package Manager, unlocking access to the thriving Swift package ecosystem, and empowering Flutter plugins to leverage a wealth of pre-built functionality to accelerate development. Once Swift Package Manager (SPM) is widely adopted by plugin developers, it should simplify the Flutter installation process itself, and lower the barrier to entry for newcomers, particularly those that aren’t familiar with the iOS ecosystem. We encourage plugin authors to [try adding support for SPM to your plugins,](https://docs.flutter.dev/packages-and-plugins/swift-package-manager/for-plugin-authors#how-to-add-swift-package-manager-support-to-an-existing-flutter-plugin) and provide [feedback](https://github.com/flutter/flutter/issues) on your experience. + +Next, we want to enable you to always say “yes” to your designers and deliver high-fidelity experiences on iOS. To address this, we’ve embarked on a journey to modernize and expand our Cupertino widget library, resolving [15 issues](https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aclosed+label%3A%22f%3A+cupertino%22+sort%3Aupdated-desc+closed%3A2024-04-01..2024-07-01+) across Cupertino, and adding 37 missing Cupertino widgets in the [widget catalog](https://docs.flutter.dev/ui/widgets/cupertino). + +Finally, we’ve added [platform view](https://docs.flutter.dev/platform-integration/macos/platform-views) and [webview](https://docs.flutter.dev/platform-integration/web/web-content-in-flutter) support for Flutter macOS apps, allowing for seamless integration of native macOS UI components directly into your Flutter app for a more complete and polished user experience. + +Looking forward, we’re excited to invest more in additional Cupertino widget fidelity, land Swift Package Manager with our ecosystem, and provide other investments that make it even easier to integrate and interoperate with Apple’s platforms. + +## Highlighting the global impact of the vibrant Flutter community + +We also want to acknowledge that these releases wouldn’t be possible without contributions from the community, including you! This set of releases consist of almost 1,500 commits from over 167 unique contributors, including 49 *new* contributors. We’re inspired by the continued high levels of activity, commitment, and growth in the Flutter community, including those actively building the framework. Thank you! + +The impact of our collective work is showing up all over the world in incredible apps and experiences that millions of people use every day. For instance, here is a sneak peek at a [case study](http://flutter.dev/showcase/xiaomi) showing how and why a small team at a Chinese technology company, Xiaomi, used Flutter to develop a companion app for the firm’s popular new EV, the [Xiaomi SU7.](https://www.mi.com/global/discover/article?id=3263&ref=renatomitra.com) + +<YoutubeEmbed id="wfD7ZQhwACU" title="Xiaomi (Flutter Developer Stories)" fullwidth="true"/> + + +Several other exciting examples of Flutter apps popping up all over the world: + +* [**SNCF Connect**](http://flutter.dev/showcase/sncf-connect), the French railway, and owners of the largest Flutter app in Europe at over 150 screens, partnered with the Olympics to deliver many updates to the Flutter app that enabled millions of visitors to travel across France during the Olympic games. + +* [**Wolt**](http://flutter.dev/showcase/wolt), part of DoorDash International, used Flutter to expand into the merchant retail market. + +* [**Whirlpool**](http://flutter.dev/showcase/whirlpool), a Fortune 500 company with a worldwide presence, is exploring new sales channels in Brazil using Flutter. + +* [**Monta**](http://flutter.dev/showcase/monta), a Danish EV charging ecosystem startup, brought their first mobile app to market in just 3 months with Flutter, and later successfully ported their web app to Flutter too. + +## Wrap up + +The above are just a few of many new features and updates to Flutter and Dart in these releases, and you can learn more in the [Flutter 3.24 technical blog](https://medium.com/p/6c040f87d1e4/edit) post and in the [Dart 3.5 blog post](https://medium.com/dartlang/dart-3.5-6ca36259fa2f). + +Looking ahead, we’re filled with excitement for the future of Flutter. We remain committed to our mission, and we’re grateful to you — whether a contributor, community member, or Flutter developer — for being part of this amazing journey. We can’t wait to see what you build next! \ No newline at end of file diff --git a/sites/www/content/blog/announcing-flutter-3-3-at-flutter-vikings/images/0La9LUctqdub-CvsS.jpg b/sites/www/content/blog/announcing-flutter-3-3-at-flutter-vikings/images/0La9LUctqdub-CvsS.jpg new file mode 100644 index 0000000000..9b47b645d8 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-3-3-at-flutter-vikings/images/0La9LUctqdub-CvsS.jpg differ diff --git a/sites/www/content/blog/announcing-flutter-3-3-at-flutter-vikings/images/0bDz2chibrm3B6QZE.webp b/sites/www/content/blog/announcing-flutter-3-3-at-flutter-vikings/images/0bDz2chibrm3B6QZE.webp new file mode 100644 index 0000000000..49ae6bbadb Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-3-3-at-flutter-vikings/images/0bDz2chibrm3B6QZE.webp differ diff --git a/sites/www/content/blog/announcing-flutter-3-3-at-flutter-vikings/index.md b/sites/www/content/blog/announcing-flutter-3-3-at-flutter-vikings/index.md new file mode 100644 index 0000000000..d4d818dbff --- /dev/null +++ b/sites/www/content/blog/announcing-flutter-3-3-at-flutter-vikings/index.md @@ -0,0 +1,55 @@ +--- +title: "Announcing Flutter 3.3 at Flutter Vikings" +description: "A new Flutter release, a beautiful new app, work on improving rendering performance, and a note of farewell" +publishDate: 2022-08-30 +author: timsneath +image: images/0bDz2chibrm3B6QZE.webp +category: events +layout: blog +--- + +### Flutter 3.3, a beautiful new app, work on improving performance, and a note of farewell + +Greetings from the beautiful Norwegian capital of Oslo, where members of the Flutter community are gathering for the [Flutter Vikings conference](https://fluttervikings.com/). Flutter Vikings is a two day community-driven developer event with three tracks of content from experts who have traveled from across the world. Over five thousand developers have registered so far, and while the in-person event is sold out, you can [watch all the sessions online for free](https://www.youtube.com/playlist?list=PL4dBIh1xps-EWXK28Qn9kiLK9-eXyqKLX). + +Flutter continues to grow, both in usage and in the size of the ecosystem. **Every day, more than 1,000 new mobile apps using Flutter are published** to the Apple and Google Play stores, along with more usage on web and desktop. And the Flutter package ecosystem [now comprises over 25,000 packages](https://pub.dev/packages?q=sdk%3Aflutter), providing further evidence of maturity and breadth. + +<DashImage figure src="images/0bDz2chibrm3B6QZE.webp" /> + + +**Today we’re announcing Flutter 3.3.** This release is focused on refinements and performance improvements that reinforce the features shipped in Flutter 3. It expands support for the evolving Material 3 specification with several new components and a number of bug fixes, and it includes new features aimed at tablet and desktop developers including Scribble handwriting support on iPad, selectable text grouping, and trackpad support. This release also includes Dart 2.18, which introduces FFI support for libraries and code written in Swift or Objective-C. Apps that are built against this release will experience improved performance across desktop, web, and mobile, so we encourage you to run flutter upgrade to get the latest on all your developer workstations! + +For more on all the new features and improvements, check out the detailed [Flutter 3.3 release notes](https://medium.com/flutter/whats-new-in-flutter-3-3-893c7b9af1ff) and the [Dart 2.18 announcement blog post](https://medium.com/dartlang/dart-2-18-f4b3101f146c). + +## Wonderous: a new reference app + +In partnership with the design team at [gskinner](https://gskinner.com/), **today we’re launching Wonderous**, an app built to showcase Flutter’s capacity to deliver high-end, beautiful experiences without caveats. Even as it shows off the power of Flutter, we built Wonderous together to be a beautiful app in its own right. From the stunning Taj Mahal in the Indian city of Agra to the Mayan ruins of Chichén Itzá on the Yucatán peninsula of Mexico, Wonderous brings some of the most remarkable places in the world to your phone, using video and imagery to explore the intersection of art, history, and culture. + +<DashImage figure src="images/0La9LUctqdub-CvsS.jpg" /> + + +Wonderous is an app we hope you’ll share with your family and friends, but is also an open source project that you can explore as a developer. As a real-world app, it provides a comprehensive example of a wide variety of visual techniques and best practices that we think will inspire intermediate to advanced developers. Over the coming weeks, the gskinner.com team will be sharing more detailed technical articles on the making of the app, including its support for accessibility, animation techniques that are generalizable, and tips and tricks for performance. + +For more about our motivations for building Wonderous and links to its associated website, the Android and iPhone apps, and the source code, check out the [separate article](https://medium.com/flutter/wonderous-explore-the-world-with-flutter-f43cce052e1) here on the Flutter blog. + +## Impeller: our new graphics engine + +Aside from the improvements in 3.3 itself, the team has been hard at work on **[Impeller](https://github.com/flutter/engine/tree/main/impeller), our next-generation rendering layer**. + +Impeller is a significant rewrite of a core part of the Flutter engine, replacing the previous Skia code with a custom runtime that takes full advantage of modern hardware-accelerated graphics APIs such as [Metal](https://developer.apple.com/metal/) on iOS and [Vulkan](https://developer.android.com/ndk/guides/graphics) on Android. It delivers silky-smooth animations, considerably raising the bar for what applications can expect from a multiplatform UI toolkit. The difference is visceral, and apps that use Impeller can expect to be able to push boundaries further than before while staying locked to 60Hz or faster refresh rates. Most notably, Impeller totally eradicates the need for runtime shader compilation, which is a common source of frame rate ‘jank’ for apps today. + +While it is not feature complete and we are still optimizing performance, we are now testing it internally on Google-class applications. And if you download [Wonderous for iPhone](https://apps.apple.com/us/app/wonderous/id1612491897) from the Apple App Store, you’ll see Impeller in action on a production-quality app. + +**We’re thrilled to now make Impeller available as an early adopter preview on iOS.** Impeller doesn’t require changes to your code, other than [enabling a flag](https://github.com/flutter/flutter/wiki/Impeller). More information on the architecture of Impeller and how to enable it [can be found on our wiki](https://github.com/flutter/flutter/wiki/Impeller). While it’s under active development, early adopters of Impeller should switch to the `master` channel of Flutter to ensure you’re working with the latest code. + +We look forward to seeing apps and demos that take advantage of the performance and quality improvements in Impeller. We’d also appreciate [reproducible issue reports](https://github.com/flutter/flutter/issues/new?assignees=&labels=created+via+performance+template&labels=impeller&template=4_performance_others.md&title=[Impeller]) that demonstrate performance or fidelity regressions in current builds. + +## Goodbye to Eric + +We close with an affectionate farewell to [Eric Seidel](https://twitter.com/_eseidel?lang=en), one of the co-founders of Flutter who is leaving Google this month for new adventures. Eric was the very first person to introduce Flutter to the world, before it had a name or a mascot, at the [Dart Developer Summit back in 2015](https://www.youtube.com/watch?v=PnIWl33YMwA); he has managed the Flutter engineering team for most of its existence, and quite simply, without Eric there would be no Flutter as we know it. + +But Eric has an entrepreneur’s heart, and his “superpower” (to use one of his favorite words) is as a creator and initiator. So it didn’t come as a huge surprise when he shared his plan to start something new. And so we send him our encouragement and appreciation as he flies the nest and begins his next adventure. + +It’s fitting to close this post with an extract from the [Flutter 1.0 launch event](https://developers.google.com/events/flutter-live), in which Eric shares our vision for Flutter: **a long-term bet that we can radically improve the experience of building beautiful user experiences**. This *remains* our vision, and we’re not done. With millions of developers from around the world relying on it, an ecosystem of thousands of contributors to the project, and a team here at Google that is flourishing and growing, we hope you’ll continue to join us on our journey! + +<YoutubeEmbed id="5SZZfpkVhwk" title="" fullwidth="true"/> diff --git a/sites/www/content/blog/announcing-flutter-3-38-dart-3-10-building-the-future-of-apps/images/1DwdxTpj5OEQzUzyj4lq6_A.gif b/sites/www/content/blog/announcing-flutter-3-38-dart-3-10-building-the-future-of-apps/images/1DwdxTpj5OEQzUzyj4lq6_A.gif new file mode 100644 index 0000000000..1ed5857c3c Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-3-38-dart-3-10-building-the-future-of-apps/images/1DwdxTpj5OEQzUzyj4lq6_A.gif differ diff --git a/sites/www/content/blog/announcing-flutter-3-38-dart-3-10-building-the-future-of-apps/index.md b/sites/www/content/blog/announcing-flutter-3-38-dart-3-10-building-the-future-of-apps/index.md new file mode 100644 index 0000000000..cef7321653 --- /dev/null +++ b/sites/www/content/blog/announcing-flutter-3-38-dart-3-10-building-the-future-of-apps/index.md @@ -0,0 +1,109 @@ +--- +title: "Announcing Flutter 3.38 & Dart 3.10: Building the future of apps" +description: "Hi, Flutter community! My name is Emma Twersky, and I am unbelievably excited to be your new lead for Flutter and Dart DevRel. I’m not just…" +publishDate: 2025-11-12 +author: twerske +image: images/1DwdxTpj5OEQzUzyj4lq6_A.gif +category: announcements +layout: blog +--- + +<DashImage figure src="images/1DwdxTpj5OEQzUzyj4lq6_A.gif" /> + + +## Announcing Flutter 3.38 & Dart 3.10: Building the future of apps + +Hi, Flutter community! My name is Emma Twersky, and I am unbelievably excited to be your new lead for Flutter and Dart DevRel. I’m not just here to talk about new features; I’m here for this community. The number one reason I’m here is for the incredible culture we have. If you haven’t read it, check out [Flutter culture and how to preserve it](https://blog.flutter.dev/flutter-culture-and-how-to-preserve-it-436b4ed1031d). It’s all about being open, modest, and respectful, all while building fantastic cross-platform apps, and that’s what Flutter and Dart are all about. I’m so excited to be a part of this community, and I can’t wait to share all that we’re working on. + +Speaking of fantastic cross-platform apps, our friends over at NotebookLM have been going viral with their Flutter app this year. We visited them to hear about how Flutter accelerated their launch, check it out: + +<YoutubeEmbed id="pMoUg3dkDJk" title="How NotebookLM Built a 4.8-Star Flutter App" fullwidth="true"/> + + +In fact, you can listen to the Audio Overview of this blog post (and all its linked sources) in [NotebookLM](https://notebooklm.google.com/notebook/be3457d4-2909-4290-9ba7-40ae015015cf?artifactId=f5b2b691-58a4-4c30-bb15-fcf944b9a255). + +Still with me? Then hold onto your keyboards, because **Flutter 3.38 and Dart 3.10 are now available**, and they are HOT. These releases are packed with usability enhancements and improvements to our developer tooling. And yes, that includes AI. + +## Building the future of apps with Google AI and the “Future of apps” series + +We’ve got a whole [Flutter Flight Plans series](https://www.youtube.com/playlist?list=PLjxrf2q8roU1yXu4k7ivSLAa0cizD4feH) dropping this month and next to show you how to build the next generation of apps with Flutter, Firebase, and Gemini. + +**Building dynamic UI with Flutter GenUI:** The new Flutter GenUI SDK is here to help you build dynamic UI that adapts based on your user’s intent. Don’t believe me? Watch the video, “Getting started with GenUI”, then check out the [genui package](https://github.com/flutter/genui) to see it in action. + +<YoutubeEmbed id="nWr6eZKM6no" title="Getting started with GenUI" fullwidth="true"/> + + +**The easiest way to build with AI:** We’ve built a sample app powered by Firebase AI logic that helps you create a nice launching point for integrating AI features into your app. This sample app covers features such as using multi-modal content generation, in addition to the Gemini conversational live API. Get your hands dirty in the [Flutter AI Playground](http://g.co/firebase/flutter-ai-playground). + +<YoutubeEmbed id="tErMAB5FKNw" title="How I built a Google I/O keynote demo in 30 minutes" fullwidth="true"/> + + +**Build multi-platform Flutter apps with Gemini CLI:** Head to [the docs](http://docs.flutter.dev/ai/create-with-ai#flutter-extension-for-gemini-cli) to jump into the Flutter extension for Gemini CLI. + +<YoutubeEmbed id="RZPkE5sllck" title="Build multiplatform apps with Gemini CLI" fullwidth="true"/> + + +## Productivity and platform power: What’s new in Flutter 3.38 and Dart 3.10 + +Now for the main event. Here’s what’s new and why you should be excited. + +<YoutubeEmbed id="-AuAZTyRelY" title="What's new in Flutter 3.38 and Dart 3.10" fullwidth="true"/> + + +### Dart 3.10 highlights: More expressive, concise, and robust code. + +* **Dot shorthands:** Less typing, more coding. Instead of `MainAxisAlignment.start`, you can now just write `.start`. Check out the [dot shorthands](https://dart.dev/language/dot-shorthands) page to learn more. + +* **Build hooks:** Now stable! Compile native code or bundle native assets directly with your Dart package. Get all the details in the [hooks](https://dart.dev/tools/hooks) page or watch the [Build hooks](https://www.youtube.com/watch?v=AxNF5dj8HWQ) video. + +* **And more!** There’s a new [analyzer plugin system](https://github.com/dart-lang/sdk/blob/main/pkg/analysis_server_plugin/doc/writing_a_plugin.md). You can use this system to write your own analysis rules and employ IDE quick fixes. Use the new [Deprecated annotation](https://api.dart.dev/stable/dart-core/Deprecated-class.html) for deprecating specific functionalities. + +Dive into the [Dart 3.10 technical blog post](https://blog.dart.dev/announcing-dart-3-10-ea8b952b6088) for the full story. + +### Flutter 3.38 highlights: A more refined framework, better platform integration, and slicker developer tools. + +* **Web enhancements:** We’ve got a [config file for `flutter run`](https://docs.flutter.dev/platform-integration/web/web-dev-config-file), proxy support, and [hot reload for the web](https://docs.flutter.dev/platform-integration/web/building#hot-reload-web). Because who has time to wait? + +* **Framework & UI:** `OverlayPortal` is more powerful than ever, and [predictive back gesture](https://developer.android.com/guide/navigation/predictive-back-gesture) is now default on Android. We’re also still polishing up Material and Cupertino. + +* **iOS updates:** Full support for iOS 26/Xcode 26, command-line deployment, and a super-important [guide for migrating Flutter apps](https://docs.flutter.dev/release/breaking-changes/uiscenedelegate#migration-guide-for-flutter-apps). + +* **Android updates:** NDK r28 is here for [16KB page size compatibility](https://developer.android.com/guide/practices/page-sizes), we’ve plugged a major memory leak, and updated to Java 17. + +* **Tooling:** Big updates to the experimental [Widget Previewer tool](https://docs.flutter.dev/tools/widget-previewer) and IDEs. Go play! + +* **Accessibility:** We’re always working to make Flutter more inclusive. Say hello to the new [`SliverSemantics`](https://api.flutter.dev/flutter/widgets/SliverSemantics-class.html) widget and better default behaviors. + +Want all the nitty-gritty details? Of course you do. Check out the [Flutter 3.38 technical blog post](https://blog.flutter.dev/whats-new-in-flutter-3-38-3f7b258f7228). + +## Community and ecosystem spotlight + +This release cycle, our community has been as active as ever, with a host of valuable contributions. Here are just a few examples: + +* **Windows display properties:** Thanks to [@9AZX](https://github.com/9AZX), you can now get detailed display information on Windows, including monitor lists, display sizes, refresh rates, and DPI. + +* **New convenience constructors:** [@ahmeddhus](https://github.com/ahmeddhus) added a `SliverGrid.list` constructor, providing a cleaner API for creating grids from a list of widgets. + +* **Enhanced gesture handling:** [@houssemeddinefadhli81](https://github.com/houssemeddinefadhli81) introduced an `onLongPressUp` callback to the `InkWell` widget to specifically handle the release of a long press gesture. + +* **More flexible widgets:** [@iamtoricool](https://github.com/iamtoricool) added a `maxCount` parameter to the `Badge.count` constructor, and [@rkishan516](https://github.com/rkishan516) added a momentary variant to `CupertinoSlidingSegmentedControl`. + +* **Important fixes:** We also saw crucial fixes for scrolling behavior from [@manu-sncf](https://github.com/manu-sncf) and [@yiiim](https://github.com/yiiim), focus issues from [@romaingyh](https://github.com/romaingyh), and more. + +💙 Let’s be real: **this community is everything**. You’re the reason Flutter is what it is today, and beyond OSS contributions to Flutter’s core, we’re constantly blown away by what you create. In this release, we’re putting the spotlight on [Kilian](https://github.com/schultek) and the framework, [Jaspr](https://jaspr.site/). Where Flutter’s web support is great for applications, Jaspr is a traditional DOM-based (HTML/CSS) web framework that is built on top of Dart. It is a great complement to Flutter web for when you want a Dart-based solution to build sites. + +We were so impressed that we migrated our entire documentation infrastructure — both dart.dev and docs.flutter.dev — over to it. Pretty cool, right? We’ve asked Kilian and the team to share the full story in a future blog post, so you’ll be hearing from them soon. In the meantime, we recommend that you [try it out](https://docs.jaspr.site/get_started/quick_start) yourself! + +## Get started! + +So, what are you waiting for? Upgrade to Flutter 3.38 and Dart 3.10 and let’s build the future together. + +* [Flutter 3.38 release notes](https://docs.flutter.dev/release/release-notes) + +* [Dart 3.10 release notes](https://github.com/dart-lang/sdk/blob/main/CHANGELOG.md) + +* Dive into [the future of AI apps](https://www.youtube.com/playlist?list=PLjxrf2q8roU3ahJVrSgAnPjzkpGmL9Czl) + +* Play in the [Flutter AI Playground](http://g.co/firebase/flutter-ai-playground) + +* Join the [flutter.dev/community](http://flutter.dev/community) \ No newline at end of file diff --git a/sites/www/content/blog/announcing-flutter-ai-toolkit/images/08PFN57i3DaWcEBvk.gif b/sites/www/content/blog/announcing-flutter-ai-toolkit/images/08PFN57i3DaWcEBvk.gif new file mode 100644 index 0000000000..8c44511e56 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-ai-toolkit/images/08PFN57i3DaWcEBvk.gif differ diff --git a/sites/www/content/blog/announcing-flutter-ai-toolkit/images/0KgA9lHvHX_FF3r50.webp b/sites/www/content/blog/announcing-flutter-ai-toolkit/images/0KgA9lHvHX_FF3r50.webp new file mode 100644 index 0000000000..9deb4e4a86 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-ai-toolkit/images/0KgA9lHvHX_FF3r50.webp differ diff --git a/sites/www/content/blog/announcing-flutter-ai-toolkit/images/1HxICVJ2oeINpsTb4DDdnqg.webp b/sites/www/content/blog/announcing-flutter-ai-toolkit/images/1HxICVJ2oeINpsTb4DDdnqg.webp new file mode 100644 index 0000000000..896a60af48 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-ai-toolkit/images/1HxICVJ2oeINpsTb4DDdnqg.webp differ diff --git a/sites/www/content/blog/announcing-flutter-ai-toolkit/images/1ctMwVabsBVXa-ppooNwGsw.webp b/sites/www/content/blog/announcing-flutter-ai-toolkit/images/1ctMwVabsBVXa-ppooNwGsw.webp new file mode 100644 index 0000000000..1d22cdca94 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-ai-toolkit/images/1ctMwVabsBVXa-ppooNwGsw.webp differ diff --git a/sites/www/content/blog/announcing-flutter-ai-toolkit/images/1gAt7fwWLXUWX_nHPwzYzxg.webp b/sites/www/content/blog/announcing-flutter-ai-toolkit/images/1gAt7fwWLXUWX_nHPwzYzxg.webp new file mode 100644 index 0000000000..bba5539aae Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-ai-toolkit/images/1gAt7fwWLXUWX_nHPwzYzxg.webp differ diff --git a/sites/www/content/blog/announcing-flutter-ai-toolkit/images/1j-2lxsZJ-WEREg7X4O7Cog.webp b/sites/www/content/blog/announcing-flutter-ai-toolkit/images/1j-2lxsZJ-WEREg7X4O7Cog.webp new file mode 100644 index 0000000000..0c2a14c501 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-ai-toolkit/images/1j-2lxsZJ-WEREg7X4O7Cog.webp differ diff --git a/sites/www/content/blog/announcing-flutter-ai-toolkit/images/1m5BA-Jaa7l9uPr9NHoWjKA.webp b/sites/www/content/blog/announcing-flutter-ai-toolkit/images/1m5BA-Jaa7l9uPr9NHoWjKA.webp new file mode 100644 index 0000000000..d3bcbb16d3 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-ai-toolkit/images/1m5BA-Jaa7l9uPr9NHoWjKA.webp differ diff --git a/sites/www/content/blog/announcing-flutter-ai-toolkit/images/1u_atZ9LOO85MJMWdLFvxow.webp b/sites/www/content/blog/announcing-flutter-ai-toolkit/images/1u_atZ9LOO85MJMWdLFvxow.webp new file mode 100644 index 0000000000..027034f860 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-ai-toolkit/images/1u_atZ9LOO85MJMWdLFvxow.webp differ diff --git a/sites/www/content/blog/announcing-flutter-ai-toolkit/index.md b/sites/www/content/blog/announcing-flutter-ai-toolkit/index.md new file mode 100644 index 0000000000..d13c3b968e --- /dev/null +++ b/sites/www/content/blog/announcing-flutter-ai-toolkit/index.md @@ -0,0 +1,89 @@ +--- +title: "Announcing Flutter AI Toolkit" +description: "Want AI chat in your Flutter app? This toolkit makes it easy." +publishDate: 2024-12-17 +author: antfitch +image: images/1u_atZ9LOO85MJMWdLFvxow.webp +category: announcements +layout: blog +--- + +Flutter AI developers, rejoice! Adding an AI-powered chat experience to your app just got a whole lot easier. Introducing [Flutter AI Toolkit](https://pub.dev/packages/flutter_ai_toolkit), a collection of ready-to-use AI chat widgets designed to seamlessly integrate into your Flutter projects. No more building complex chat interfaces from scratch — Flutter AI Toolkit provides everything you need to quickly and easily add a sophisticated AI chat window to your app. Ready to boost customer engagement and satisfaction across multiple platforms? You’ve got this! + +<DashImage figure src="images/08PFN57i3DaWcEBvk.gif" alt="[Try Flutter AI Toolkit in Project IDX!](https://idx.google.com/new?template=https%3A%2F%2Fgithub.com%2Fflutter%2Fai)" caption="[Try Flutter AI Toolkit in Project IDX!](https://idx.google.com/new?template=https%3A%2F%2Fgithub.com%2Fflutter%2Fai)" /> + + +## Multi-turn chat + +With multi-turn chat, you can maintain context across multiple interactions in the same session. + +Imagine you’re talking to a friend. They ask if you have a pet and you tell them that you have a pet rock named Rocky. A little while later after chatting with you about other things, your friend asks “How long have you had Rocky?” The friend remembers your pet rock from an earlier conversation, even though you didn’t mention it again. + +That’s essentially what multi-turn chat does! It helps the LLM (generative AI Large Language Model) remember what is being talked about. + +<DashImage figure src="images/1u_atZ9LOO85MJMWdLFvxow.webp" alt="Multi-turn chat in Flutter AI Toolkit" caption="Multi-turn chat in Flutter AI Toolkit" /> + + +## Rich text display + +Rich-text format is supported in the responses produced by the LLM. Instead of producing just plain-text words, the LLM can format the text to be more interesting to read. For example, the response could include bold text, images, bullets, and more. + +<DashImage figure src="images/1m5BA-Jaa7l9uPr9NHoWjKA.webp" alt="Rich-format support in Flutter AI Toolkit" caption="Rich-format support in Flutter AI Toolkit" /> + + +## Voice input + +Voice input can be used to convert messages to text. This hands-free option is perfect for those who prefer not to type or who want a faster way to compose messages. + +<DashImage figure src="images/0KgA9lHvHX_FF3r50.webp" alt="Voice input support in Flutter AI Toolkit" caption="Voice input support in Flutter AI Toolkit" /> + + +## Multimedia attachments + +Pictures, videos, audio, PDFs, and other files can be added to conversations separately or all together. + +<DashImage figure src="images/1HxICVJ2oeINpsTb4DDdnqg.webp" alt="Multimedia attachment support in Flutter AI Toolkit" caption="Multimedia attachment support in Flutter AI Toolkit" /> + + +## Custom response widgets + +Design specialized UI components for responses. For example, if someone requests a recipe, you can show an interactive recipe card with an option to save the recipe directly to a database. + +<DashImage figure src="images/1j-2lxsZJ-WEREg7X4O7Cog.webp" alt="Custom AI response widget support in Flutter AI Toolkit" caption="Custom AI response widget support in Flutter AI Toolkit" /> + + +## Custom styling + +Configure the chat appearance to match your app. Change the colors of the chat bubbles, the background, the UI fonts, and even add UI pictures or animations to reflect your brand identity. + +<DashImage figure src="images/1ctMwVabsBVXa-ppooNwGsw.webp" alt="Custom styling support in Flutter AI Toolkit" caption="Custom styling support in Flutter AI Toolkit" /> + + +## Cross-platform support + +Flutter supports many platforms and so does Flutter AI Toolkit. Want to deploy an AI chat app made with Flutter on iOS, Android, web, and macOS? No problem! + +## Pluggable LLMs + +Flutter AI Toolkit makes it easy to add AI to your app, whether you want to use Google’s Gemini or Vertex AI, or even another LLM. GeminiProvider and VertexProvider are available for out-of-the-box integration, using the [Google AI Dart SDK](https://pub.dev/packages/google_generative_ai) and [Vertex AI in Firebase SDK](https://pub.dev/packages/firebase_vertexai) respectively. We highly recommend that you use the Vertex AI in Firebase SDK for production use cases beyond prototyping. In addition, you can leverage Flutter AI Toolkit’s LLM provider interface to plug in the LLM of your choice with your own custom code. + +## Stand-alone chat app with multiple conversations + +Build a full-fledged chat app that can store and manage multiple conversations. To help you get started, Flutter AI Toolkit comes with a sample project that leverages a [Cloud Firestore](https://firebase.google.com/docs/firestore) database and [Vertex AI in Firebase](https://firebase.google.com/docs/vertex-ai). + +<DashImage figure src="images/1gAt7fwWLXUWX_nHPwzYzxg.webp" alt="Flutter AI Chat App" caption="Flutter AI Chat App" /> + + +## Try Flutter AI Toolkit now + +To try Flutter AI Toolkit yourself, check out [Flutter AI Toolkit on pub.dev](https://pub.dev/packages/flutter_ai_toolkit) and the following resources: + +* [Flutter AI Toolkit documentation](https://docs.flutter.dev/ai-toolkit/user-experience) + +* [Flutter AI Toolkit interactive demo](https://flutter-ai-toolkit-examp-60bad.web.app/) ([GitHub](https://github.com/flutter/ai/blob/main/example/lib/demo/demo.dart)) + +* [Recipe example project (GitHub)](https://github.com/flutter/ai/tree/main/example/lib/recipes) + +* [Flutter AI Chat sample app (GitHub)](https://github.com/csells/flutter_ai_chat/blob/main/README.md) + +* [Get started in the browser (Project IDX)](https://idx.google.com/new?template=https%3A%2F%2Fgithub.com%2Fflutter%2Fai) \ No newline at end of file diff --git a/sites/www/content/blog/announcing-flutter-beta-1-build-beautiful-native-apps/images/0QcURHTZejNbiWHwB.webp b/sites/www/content/blog/announcing-flutter-beta-1-build-beautiful-native-apps/images/0QcURHTZejNbiWHwB.webp new file mode 100644 index 0000000000..fe474fa33b Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-beta-1-build-beautiful-native-apps/images/0QcURHTZejNbiWHwB.webp differ diff --git a/sites/www/content/blog/announcing-flutter-beta-1-build-beautiful-native-apps/images/1VIYiGwYgYQAebs0ouJTW6g.webp b/sites/www/content/blog/announcing-flutter-beta-1-build-beautiful-native-apps/images/1VIYiGwYgYQAebs0ouJTW6g.webp new file mode 100644 index 0000000000..72bd837e4b Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-beta-1-build-beautiful-native-apps/images/1VIYiGwYgYQAebs0ouJTW6g.webp differ diff --git a/sites/www/content/blog/announcing-flutter-beta-1-build-beautiful-native-apps/images/1qo6Fo51uAmD3B3d6VfcbAQ.webp b/sites/www/content/blog/announcing-flutter-beta-1-build-beautiful-native-apps/images/1qo6Fo51uAmD3B3d6VfcbAQ.webp new file mode 100644 index 0000000000..7a52be966c Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-beta-1-build-beautiful-native-apps/images/1qo6Fo51uAmD3B3d6VfcbAQ.webp differ diff --git a/sites/www/content/blog/announcing-flutter-beta-1-build-beautiful-native-apps/index.md b/sites/www/content/blog/announcing-flutter-beta-1-build-beautiful-native-apps/index.md new file mode 100644 index 0000000000..97a71aa822 --- /dev/null +++ b/sites/www/content/blog/announcing-flutter-beta-1-build-beautiful-native-apps/index.md @@ -0,0 +1,78 @@ +--- +title: "Announcing Flutter beta 1: Build beautiful native apps" +description: "Today, as part of Mobile World Congress 2018, we are excited to announce the first beta release of Flutter. Flutter is Google’s new mobile…" +publishDate: 2018-02-27 +author: sethladd +image: images/1qo6Fo51uAmD3B3d6VfcbAQ.webp +category: releases +layout: blog +--- + +Today, as part of Mobile World Congress 2018, we are excited to announce the first beta release of [Flutter](https://flutter.io/?utm_source=google&utm_medium=blog&utm_campaign=beta_announcement). Flutter is Google’s new mobile UI framework that helps developers craft high-quality native interfaces for both iOS and Android. [Get started](https://flutter.io/get-started/?utm_source=google&utm_medium=blog&utm_campaign=beta_get_started) today at [flutter.io](https://flutter.io/?utm_source=google&utm_medium=blog&utm_campaign=beta_announcement) to build beautiful native apps in record time. + +<YoutubeEmbed id="fq4N0hgOWzU" title="Introducing Flutter" fullwidth="true"/> + + +Flutter targets the sweet spot of mobile development: performance and platform integrations of native mobile, with high-velocity development and multi-platform reach of portable UI toolkits. + +<DashImage figure src="images/1qo6Fo51uAmD3B3d6VfcbAQ.webp" /> + + +Designed for both new and experienced mobile developers, Flutter can help you build beautiful and successful apps in record time with benefits such as: + +* **High-velocity development** with features like stateful Hot Reload, a new reactive framework, rich widget set, and integrated tooling. + +* **Expressive and flexible designs** with composable widget sets, rich animation libraries, and a layered, extensible architecture. + +* **High-quality experiences** across devices and platforms with our portable, GPU-accelerated renderer and high-performance, native ARM code runtime. + +Since our alpha release last year, we delivered, with help from our community, features such as screen reader support and other accessibility features, right-to-left text, localization and internationalization, iPhone X and iOS 11 support, inline video, additional image format support, running Flutter code in the background, and much more. + +Our tools also improved significantly, with support for [Android Studio](https://plugins.jetbrains.com/plugin/9212-flutter), [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code), new refactorings to help you manage your widget code, platform interop to expose the power of mobile platforms to Flutter code, improved stateful hot reloads, and a new [widget inspector](https://flutter.io/inspector/) to help you browse the widget tree. + +<DashImage figure src="images/0QcURHTZejNbiWHwB.webp" alt="Flutter’s new widget inspector helps you visualize your widget tree." caption="Flutter’s new widget inspector helps you visualize your widget tree." /> + + +Thanks to the many new features across the framework and tools, teams across Google (such as AdWords) and around the world have been successful with Flutter. Flutter has been used in production apps with millions of installs, apps built with Flutter have been featured in the App Store and Play Store (for example, [Hamilton: The Musical](https://www.blog.google/topics/developers/it-must-be-nice-have-hamilton-your-phone/)), and startups and agencies have been successful with Flutter. + +For example, [Codemate](https://www.codemate.com/), a development agency in Finland, attributes Flutter’s high-velocity dev cycle and customizable UI toolkit to their ability to quickly build a beautiful app for [Hookle](https://www.hookle.net/). “We now confidently recommend Flutter to help our clients perform better and deliver more value to their users across mobile.”, said Toni Piirainen, CEO of Codemate. + +<DashImage figure src="images/1VIYiGwYgYQAebs0ouJTW6g.webp" alt="Apps built with Flutter deliver quality, performance, and customized designs across platforms." caption="Apps built with Flutter deliver quality, performance, and customized designs across platforms." /> + + +Flutter’s beta also [works with a pre-release](https://github.com/flutter/flutter/wiki/Trying-the-preview-of-Dart-2-in-Flutter) of [Dart 2](https://medium.com/@asandholm/announcing-dart-2-80ba01f43b6), with improved support for declaring UI in code with minimal language ceremony. For example, Dart 2 infers `new` and `const` to remove boilerplate when building UI. Here is an example: + +```dart +// Before Dart 2 +Widget build(BuildContext context) { + return new Container( + height: 56.0, + padding: const EdgeInsets.symmetric(horizontal: 8.0), + decoration: new BoxDecoration(color: Colors.blue[500]), + child: new Row( + ... + ), + ); +} + +// After Dart 2 +Widget build(BuildContext context) => + Container( + height: 56.0, + padding: EdgeInsets.symmetric(horizontal: 8.0), + decoration: BoxDecoration(color: Colors.blue[500]), + child: Row( + ... + ), + ); +``` + +We’re thrilled to see Flutter’s ecosystem thriving. There are now over [1000 packages that work with Flutter](https://pub.dartlang.org/flutter) (for example: [SQLite](https://pub.dartlang.org/packages/sqflite), [Firebase](https://github.com/flutter/plugins/blob/master/FlutterFire.md), [Facebook Connect](https://pub.dartlang.org/packages/flutter_facebook_connect), [shared preferences](https://pub.dartlang.org/packages/shared_preferences), [GraphQL](https://pub.dartlang.org/packages/graphql_client), and lots more), over 1700 people in our [chat](https://gitter.im/flutter/flutter), and we’re delighted to see our community launch new sites such as [Flutter Institute](https://flutter.institute/), [Start Flutter](http://startflutter.com/), and [Flutter Rocks](https://flutter.rocks/). Plus, you can now subscribe to the new [Flutter Weekly newsletter](https://flutterweekly.net/), edited and published by our community. + +As we look forward to our 1.0 release, we are focused on stabilization and scenario completion. Our roadmap, largely influenced by our community, currently tracks features such as making it easier to [embed Flutter into an existing app](https://github.com/flutter/flutter/issues/14821), [inline WebView](https://github.com/flutter/flutter/issues/730), [improved routing and navigation APIs](https://github.com/flutter/flutter/labels/prod%3A%20routes), [additional Firebase support](https://github.com/flutter/flutter/issues?q=is%3Aopen+is%3Aissue+label%3A%22plugin%3A+firebase%22?), [inline maps](https://github.com/flutter/flutter/issues/73), a [smaller core engine](https://github.com/flutter/flutter/issues/12456), and more. We expect to release new betas approximately every four weeks, and we highly encourage you to vote (👍) on issues important to you and your app via our [issue tracker](https://flutter.io/issues). + +Now is the perfect time to try Flutter. You can go from zero to your first running Flutter app quickly with our [Getting Started guide](https://flutter.io/get-started/?utm_source=google&utm_medium=blog&utm_campaign=beta_get_started). If you already have Flutter installed, you can switch to the beta channel using [these instructions](https://github.com/flutter/flutter/wiki/Flutter-build-release-channels). + +We want to extend our sincere thanks for your support, feedback, and many contributions. We look forward to continuing this journey with everyone, and we can’t wait to see what you build! + +*Hello, developers in China! This post is also [available in Chinese](https://developers.googleblog.cn/2018/02/flutter-beta1.html).* \ No newline at end of file diff --git a/sites/www/content/blog/announcing-flutter-for-windows/images/02FlTNeXvMtnATYfw.webp b/sites/www/content/blog/announcing-flutter-for-windows/images/02FlTNeXvMtnATYfw.webp new file mode 100644 index 0000000000..424330f5eb Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-for-windows/images/02FlTNeXvMtnATYfw.webp differ diff --git a/sites/www/content/blog/announcing-flutter-for-windows/images/0CV_R70NqCzCoGxuI.webp b/sites/www/content/blog/announcing-flutter-for-windows/images/0CV_R70NqCzCoGxuI.webp new file mode 100644 index 0000000000..d71833a43c Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-for-windows/images/0CV_R70NqCzCoGxuI.webp differ diff --git a/sites/www/content/blog/announcing-flutter-for-windows/images/0EgO7uhhHTZ5M5RWX.webp b/sites/www/content/blog/announcing-flutter-for-windows/images/0EgO7uhhHTZ5M5RWX.webp new file mode 100644 index 0000000000..4996892c6b Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-for-windows/images/0EgO7uhhHTZ5M5RWX.webp differ diff --git a/sites/www/content/blog/announcing-flutter-for-windows/images/0EokU2CUDhjr6nDpW.webp b/sites/www/content/blog/announcing-flutter-for-windows/images/0EokU2CUDhjr6nDpW.webp new file mode 100644 index 0000000000..3bba6aa68f Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-for-windows/images/0EokU2CUDhjr6nDpW.webp differ diff --git a/sites/www/content/blog/announcing-flutter-for-windows/images/0EqTtZLTkDhGrNAZj.webp b/sites/www/content/blog/announcing-flutter-for-windows/images/0EqTtZLTkDhGrNAZj.webp new file mode 100644 index 0000000000..8fe1f01066 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-for-windows/images/0EqTtZLTkDhGrNAZj.webp differ diff --git a/sites/www/content/blog/announcing-flutter-for-windows/images/0LtCDFBQudFeDS_f6.jpg b/sites/www/content/blog/announcing-flutter-for-windows/images/0LtCDFBQudFeDS_f6.jpg new file mode 100644 index 0000000000..8da43d4c64 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-for-windows/images/0LtCDFBQudFeDS_f6.jpg differ diff --git a/sites/www/content/blog/announcing-flutter-for-windows/images/0QZlDI5SemNqlOuX1.webp b/sites/www/content/blog/announcing-flutter-for-windows/images/0QZlDI5SemNqlOuX1.webp new file mode 100644 index 0000000000..47500d1cc7 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-for-windows/images/0QZlDI5SemNqlOuX1.webp differ diff --git a/sites/www/content/blog/announcing-flutter-for-windows/images/0nmWy9ouW6nL5vrlB.webp b/sites/www/content/blog/announcing-flutter-for-windows/images/0nmWy9ouW6nL5vrlB.webp new file mode 100644 index 0000000000..aa3a46e251 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-for-windows/images/0nmWy9ouW6nL5vrlB.webp differ diff --git a/sites/www/content/blog/announcing-flutter-for-windows/images/0vowtRZE_wvyVA7CB.webp b/sites/www/content/blog/announcing-flutter-for-windows/images/0vowtRZE_wvyVA7CB.webp new file mode 100644 index 0000000000..2a906c89ff Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-for-windows/images/0vowtRZE_wvyVA7CB.webp differ diff --git a/sites/www/content/blog/announcing-flutter-for-windows/images/0w2gBpEJcFceqjIRv.webp b/sites/www/content/blog/announcing-flutter-for-windows/images/0w2gBpEJcFceqjIRv.webp new file mode 100644 index 0000000000..5602465ad1 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-for-windows/images/0w2gBpEJcFceqjIRv.webp differ diff --git a/sites/www/content/blog/announcing-flutter-for-windows/images/1IDu8nsxUNLecfET3nuSdDg.webp b/sites/www/content/blog/announcing-flutter-for-windows/images/1IDu8nsxUNLecfET3nuSdDg.webp new file mode 100644 index 0000000000..6c4515cbf9 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-for-windows/images/1IDu8nsxUNLecfET3nuSdDg.webp differ diff --git a/sites/www/content/blog/announcing-flutter-for-windows/images/1UuEJrQgjNUXhxgDJgjhQLg.jpeg b/sites/www/content/blog/announcing-flutter-for-windows/images/1UuEJrQgjNUXhxgDJgjhQLg.jpeg new file mode 100644 index 0000000000..897d099516 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-for-windows/images/1UuEJrQgjNUXhxgDJgjhQLg.jpeg differ diff --git a/sites/www/content/blog/announcing-flutter-for-windows/images/1kZRGSKMDQQhbINmZRw6z4w.webp b/sites/www/content/blog/announcing-flutter-for-windows/images/1kZRGSKMDQQhbINmZRw6z4w.webp new file mode 100644 index 0000000000..19024db8a2 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-for-windows/images/1kZRGSKMDQQhbINmZRw6z4w.webp differ diff --git a/sites/www/content/blog/announcing-flutter-for-windows/index.md b/sites/www/content/blog/announcing-flutter-for-windows/index.md new file mode 100644 index 0000000000..4426c49b8f --- /dev/null +++ b/sites/www/content/blog/announcing-flutter-for-windows/index.md @@ -0,0 +1,115 @@ +--- +title: "Announcing Flutter for Windows" +description: "Build high-quality Windows apps that also run on mobile and web" +publishDate: 2022-02-03 +author: timsneath +image: images/0LtCDFBQudFeDS_f6.jpg +category: announcements +layout: blog +--- + +Since we launched Flutter, we’ve focused on delivering a cross-platform solution for beautiful, tailored apps that are compiled to machine code and take full advantage of the underlying graphics hardware of your device. Today marks a significant expansion of this vision with the first production release of support for Windows as an app target, enabling Windows developers to benefit from the same productivity and power that mobile developers have been enjoying. + +<DashImage figure src="images/0LtCDFBQudFeDS_f6.jpg" /> + + +Our goal with Flutter is to give you the tools you need to build a great experience, regardless of which operating system you’re building for. And so we want to bring the same core framework and tools to every place you might want to paint pixels. Flutter allows you to handcraft **beautiful** experiences where your brand and design come to the forefront. Flutter is **fast**, compiling directly to machine code; with support for stateful hot reload, you get the **productivity** of an interactive environment that allows you to make changes while your app is running and see the results immediately. And Flutter is **open**, with thousands of contributors adding to the core framework and extending it with an ecosystem of packages. +> # Nearly half a million apps use Flutter… + +So far, we’ve seen momentum that has exceeded our expectations, with nearly half a million apps now released that use Flutter, including big apps from companies like [Betterment](https://verygood.ventures/success-stories/betterment), BMW, and ByteDance, and apps from thirty teams at Google. In 2021, Flutter became the most popular cross-platform UI toolkit, as measured by analysts like [Statista](https://www.statista.com/) and [SlashData](https://www.slashdata.co/): + +<DashImage figure src="images/0w2gBpEJcFceqjIRv.webp" /> + + +Our own data backs this up, with a consistent [92% of Flutter developers expressing positive satisfaction](https://medium.com/flutter/about-web-community-and-code-samples-q3-2021-survey-results-b67f5b997dca) with our tools in all four quarterly surveys in 2021. (And to the other 8% of you, we’re listening to your feedback and want you to be happy as well!) + +One common request has been for Windows support. +> # Today, we’re thrilled to announce the full availability of support for Windows apps for Flutter in stable builds. + +## Windows and Flutter + +A couple of years ago, we laid out an ambitious vision for Flutter to expand from mobile apps on iOS and Android to other platforms including web and the *desktop*. + +The core of Flutter carries across platforms: from the portable, hardware-accelerated Skia graphics engine, to the Flutter rendering system; core primitives like animation, theming, text input, and internationalization; and the hundreds of widgets that Flutter offers. + +But desktop apps aren’t just mobile apps running on a bigger screen. They’re designed for different input devices, such as a keyboard and mouse. They have resizable windows that often run on a widescreen monitor. There are different conventions for critical things like accessibility, input method editors, and visual styling. And they integrate with different APIs in the underlying operating system: desktop apps support everything from file system pickers to device hardware to data stores like the Windows registry. + +So while we’ve brought Flutter *to* Windows, we’ve also tailored it *for* Windows. + +Just as with our support for Android and iOS, the Windows implementation of Flutter combines a Dart framework and C++ engine. Windows and Flutter communicate through an embedding layer that hosts the Flutter engine and is responsible for translating and dispatching Windows messages. Flutter coordinates with Windows to paint your UI to the screen, handles events like window resizing and DPI changes, and works with existing Windows modalities for internationalization, such as input method editors. + +<DashImage figure src="images/0nmWy9ouW6nL5vrlB.webp" alt="On Windows, Flutter uses exactly the same Dart code, but takes advantage of native Windows APIs." caption="On Windows, Flutter uses exactly the same Dart code, but takes advantage of native Windows APIs." /> + + +Your app can use every part of the Flutter framework, and on Windows, it can also talk to the Win32, COM, and Windows Runtime APIs either directly through Dart’s C interop layer, or using a platform plugin written in C++. We’ve also adapted a number of common plugins to include Windows support, including `camera`, `file_picker`, and `shared_preferences`. More importantly, the community has already added Windows support for a broad array of other packages, covering everything from Windows taskbar integration to serial port access. + +<DashImage figure src="images/0EgO7uhhHTZ5M5RWX.webp" alt="There are already hundreds of packages that have been adapted to support Flutter apps built for Windows." caption="There are already hundreds of packages that have been adapted to support Flutter apps built for Windows." /> + + +For a fully tailored Windows UI, you can also use packages like [`fluent_ui`](https://pub.dev/packages/fluent_ui) and [`flutter_acrylic`](https://pub.dev/packages/flutter_acrylic) to create an app that expresses the Microsoft Fluent design system beautifully. And using the [`msix`](https://pub.dev/packages/msix) tool you can wrap your app in an installer that can be uploaded to the Microsoft Store on Windows. + +Together, this fosters creation of apps that look great on Windows, run fast on Windows, and still transfer to other desktop or mobile devices, as well as the web. Here are a few early examples that we’ve seen so far: + +<DashImage figure src="images/1UuEJrQgjNUXhxgDJgjhQLg.jpeg" /> + + +<DashImage figure src="images/0CV_R70NqCzCoGxuI.webp" /> + + +<DashImage figure src="images/0EokU2CUDhjr6nDpW.webp" /> + + +<DashImage figure src="images/02FlTNeXvMtnATYfw.webp" /> + + +<DashImage figure src="images/0QZlDI5SemNqlOuX1.webp" /> + + +<DashImage figure src="images/0EqTtZLTkDhGrNAZj.webp" alt="Some early community examples of Windows apps built with Flutter, including [Harmonoid](https://github.com/harmonoid/harmonoid) and [Rows](https://rows.com/)." caption="Some early community examples of Windows apps built with Flutter, including [Harmonoid](https://github.com/harmonoid/harmonoid) and [Rows](https://rows.com/)." /> + + +## Microsoft and Flutter + +We asked the Windows team if they’d be willing to share a few words about Flutter’s support. Here’s what Kevin Gallo, Corporate Vice President for Windows Developer Platform at Microsoft, has to say: +> “We’re delighted to see Flutter adding support for creating Windows apps. Windows is an open platform, and we welcome all developers. We’re excited to see Flutter developers bring their experiences to Windows and also publish to the Microsoft Store. Flutter support for Windows is a big step for the community, and we can’t wait to see what you’ll bring to Windows!” + +Indeed, several teams from Microsoft have contributed to today’s announcement. In particular, we’d like to express our gratitude to the Fluent design team for their contribution of iconography for Flutter apps on Windows. Their [`fluentui_system_icons`](https://pub.dev/packages/fluentui_system_icons) package has been awarded Flutter Favorite status to signify its quality. + +We’ve also been impressed with the investments Microsoft has made around accessibility for Windows, and we’re grateful for that team’s assistance to ensure Flutter has support for screen readers from day one. It’s a mistake to dismiss accessibility as a niche requirement. As this diagram from the [Microsoft inclusive design toolkit](https://www.microsoft.com/design/inclusive/) illustrates, we all have reason to care about delivering experiences that adapt for different permanent, temporary, or situational needs. + +<DashImage figure src="images/1kZRGSKMDQQhbINmZRw6z4w.webp" /> + + +The video below demonstrates how Flutter integrates with Windows Narrator. For the purposes of this video, we’ve deliberately blurred the screen to give you a sense of how valuable this feature is to users who need it. + +<YoutubeEmbed id="YVSlpqSXV0c" title="Flutter Windows Narrator Demo" fullwidth="true"/> + + +## An ecosystem of tools for Windows development + +Our tooling partners are also adding support for Windows. To highlight a few: + +* [FlutterFlow](https://flutterflow.io/), the low-code Flutter app design tool, is [announcing support today for Windows](https://www.youtube.com/watch?v=6jnXHgSQ-Q0), along with features to help Flutter developers build apps tailored for desktop usage. + +* [Realm](https://docs.mongodb.com/realm/sdk/flutter/) is a super-fast local data store. [The latest version, shipping today](https://www.mongodb.com/developer/article/introducing-realm-flutter-sdk/), now supports building Windows apps with Flutter, with fast access to the underlying database using [Dart FFI](https://dart.dev/guides/libraries/c-interop), adding to their existing support for mobile platforms like iOS and Android. + +* Nevercode has updated their [Codemagic CI/CD tool](https://flutterci.com/) to support Windows, enabling you to test and build your Windows apps in the cloud and automate deployment of your app to the Microsoft Store. + +* [Syncfusion](https://www.syncfusion.com/flutter-widgets) have updated their suite of widgets to take full advantage of Windows. If you subscribe to their toolkit, you’ll find data visualization components like treemaps and charting, a rich data grid widget, calendars and even support for PDF creation and Excel spreadsheets. + +* Lastly, [Rive](https://rive.app/) announced today an upcoming Windows version of their popular graphics tooling suite, allowing designers and developers to create interactive vector animations that can respond to code in real time using a state machine. The upcoming Windows edition of their app offers screaming fast performance and a lower memory footprint, and will be available soon in the Microsoft Store for download. + +<DashImage figure src="images/1IDu8nsxUNLecfET3nuSdDg.webp" alt="[Rive](https://rive.app), coming soon to the Microsoft Store on Windows." caption="[Rive](https://rive.app), coming soon to the Microsoft Store on Windows." /> + + +It’s very exciting for us to see a mature ecosystem built around Flutter, and we’d encourage you to check out each of these partners as you start building Windows apps with Flutter. + +## Windows support arrives in Flutter 2.10 + +Stable, production-quality support for building Windows apps is available as part of [Flutter 2.10, which releases today](https://docs.flutter.dev/get-started/install). Flutter 2.10 also includes many other features, performance improvements and bug fixes, which are covered in more detail [in a separate blog post](https://medium.com/flutter/whats-new-in-flutter-2-10-5aafb0314b12). + +In the coming months, you’ll hear more from us on completing stable support for macOS and Linux, making the full set of desktop, web, and mobile platforms available for your production Flutter apps. + +In the meantime, thank you for your support of Flutter. We’re excited to see what you build for Windows! + +<DashImage figure src="images/0vowtRZE_wvyVA7CB.webp" /> diff --git a/sites/www/content/blog/announcing-flutter-forward/images/02u_pjVJlgpQw4XHy.webp b/sites/www/content/blog/announcing-flutter-forward/images/02u_pjVJlgpQw4XHy.webp new file mode 100644 index 0000000000..4723c4e54e Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-forward/images/02u_pjVJlgpQw4XHy.webp differ diff --git a/sites/www/content/blog/announcing-flutter-forward/index.md b/sites/www/content/blog/announcing-flutter-forward/index.md new file mode 100644 index 0000000000..c4932a27b6 --- /dev/null +++ b/sites/www/content/blog/announcing-flutter-forward/index.md @@ -0,0 +1,50 @@ +--- +title: "Announcing Flutter Forward" +description: "Register for the next Flutter event, streaming live from Nairobi, Kenya" +publishDate: 2022-11-15 +author: kkboateng +image: images/02u_pjVJlgpQw4XHy.webp +category: events +layout: blog +--- + +<DashImage figure src="images/02u_pjVJlgpQw4XHy.webp" /> + + +Tune in for **Flutter Forward**, the next global Flutter event, streaming live from Nairobi, Kenya on January 25, 2023. + +[Registration is now open!](https://rsvp.withgoogle.com/events/flutter-forward-2022-virtual/forms/registration) + +With the launch of Flutter 3 at Google I/O this year, we completed [a journey that we first mapped out four years ago at Flutter Live](https://www.youtube.com/watch?v=kpcjBD1XDwU&t=108s): delivering a powerful, open source UI toolkit for building stunning experiences across mobile, desktop, embedded, and web devices. + +Now we’re ready to share some new investments we’re working on, which showcase the direction we are planning to take Flutter over the coming year or so. + +We can’t wait to bring our global community together virtually, like we did at Flutter Live and Flutter Interact. And what better place to talk about the technologies of tomorrow than in an area of the world that is undergoing rapid digital transformation? + +We’re excited to host our next global event in Africa. Not only is Google heavily investing in the region, including opening our first [African engineering center in Nairobi](https://techcrunch.com/2022/04/19/google-opens-product-development-center-in-nairobi-the-first-in-africa/?guccounter=1&guce_referrer=aHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS8&guce_referrer_sig=AQAAAKYdxE7OdnBWrzyv5kgve4Wb_0sT7guGi_DeKOZ2_sFPBEmeJ_wsHSh077n6xrk83OtPuikdhfQ0zPKU1H3cXhqeZQcMY9U54t3eB4otQyCj3-C_xP_JzbwQMDCCRF5wi9xqDcCverqQE0l2gNazZIsKqGuT1oA5Hmwd9zd-5zJ0), but the [startup scene is booming](https://thebigdeal.substack.com/p/rain-always-falls-where-its-wet), and [hundreds of thousands of developers work in the African Internet economy](https://nextbillionusers.google/research/africa-developer-community-2021/). It’s the perfect place to give you a peek at the future of Flutter. + +Here’s what you can expect from the event: + +## Keynote + +During our live keynote, we’ll demo what we’re working on — and show what others in the broader community are doing with Flutter — to push UI development forward. + +## #AskFlutter live + +#AskFlutter will be back, answering all the questions you’re yearning to ask, from platform-specific inquiries to why Dash must always have a sharp beak, never curved. + +## Tech talks + +As always, we’ll expand on the keynote with a collection of tech talks covering the latest news, hot topics, and tips and tricks for building amazing experiences with Flutter. + +## Watch parties + +If you want to gather with others to watch Flutter Forward together, attend a local viewing party in one of over 300 cities all over the globe. If one is happening near you, be sure to attend to network with and learn from local Flutter developers. + +## Stay tuned + +The community has grown considerably since our last event, but it remains Flutter’s lifeblood, and is the momentum behind Flutter’s ever-unfolding story. We’re beyond grateful for the opportunity to bring the global Flutter community together again. + +We’ve got plenty planned between now and January 25, so [register now](https://rsvp.withgoogle.com/events/flutter-forward-2022-virtual/forms/registration) to be sure you don’t miss the agenda announcement, a chance to find a watch party near you, or a couple of special surprises we’ve got planned. + +Once you’ve registered, follow us on [Twitter](https://twitter.com/FlutterDev), [LinkedIn](https://www.linkedin.com/showcase/flutterdevofficial/), and subscribe to the Flutter [YouTube](https://www.youtube.com/c/flutterdev) channel to be sure you don’t miss any more of Dash’s multiverse adventures. \ No newline at end of file diff --git a/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/0dKv6teOS8d4zjiWk.webp b/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/0dKv6teOS8d4zjiWk.webp new file mode 100644 index 0000000000..505106e629 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/0dKv6teOS8d4zjiWk.webp differ diff --git a/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/0isPy4BXVD5J5hyQg.webp b/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/0isPy4BXVD5J5hyQg.webp new file mode 100644 index 0000000000..614896e922 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/0isPy4BXVD5J5hyQg.webp differ diff --git a/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/0uMT4fCQFO8VQwB0Q.webp b/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/0uMT4fCQFO8VQwB0Q.webp new file mode 100644 index 0000000000..bf1a8bf02c Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/0uMT4fCQFO8VQwB0Q.webp differ diff --git a/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/0vktUouQPnTpgk0Ko.webp b/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/0vktUouQPnTpgk0Ko.webp new file mode 100644 index 0000000000..dd6a31093f Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/0vktUouQPnTpgk0Ko.webp differ diff --git a/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/0y2IpiLOI01i9BqiC.webp b/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/0y2IpiLOI01i9BqiC.webp new file mode 100644 index 0000000000..cb55d36659 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/0y2IpiLOI01i9BqiC.webp differ diff --git a/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/0yW96xpHMveP4-kUm.webp b/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/0yW96xpHMveP4-kUm.webp new file mode 100644 index 0000000000..14086800cd Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/0yW96xpHMveP4-kUm.webp differ diff --git a/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/1ghwj9i6N9nvBvNmKdP8oZw.gif b/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/1ghwj9i6N9nvBvNmKdP8oZw.gif new file mode 100644 index 0000000000..a8c6f94591 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/images/1ghwj9i6N9nvBvNmKdP8oZw.gif differ diff --git a/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/index.md b/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/index.md new file mode 100644 index 0000000000..f72a585fea --- /dev/null +++ b/sites/www/content/blog/announcing-flutter-linux-alpha-with-canonical/index.md @@ -0,0 +1,217 @@ +--- +title: "Announcing Flutter Linux Alpha with Canonical" +description: "The Ubuntu team has produced a new GTK+ based host for Flutter apps on all Linux distros" +publishDate: 2020-07-08 +author: csells +image: images/0uMT4fCQFO8VQwB0Q.webp +category: spotlight +layout: blog +--- + +## Canonical enables Linux desktop app support with Flutter + +*By Chris Sells (Google) & Ken VanDine (Canonical)* + +Google’s goal for Flutter has always been to provide a portable toolkit for building beautiful UIs that run at native speeds, no matter which platform you target. To validate this capability, we started by focusing on the mobile platforms Android and iOS, where we’ve seen more than 80,000 fast, beautiful Flutter apps published to Google Play. + +To build on this success, for more than a year we’ve been expanding our focus to include desktop-class experiences, both for the web and for the desktop operating systems: macOS, Windows, and Linux. This work includes extensive refactoring of the engine to support desktop-style mouse and keyboard input, as well as resizable top-level windows. It also includes new UI capabilities that adapt well to desktop, like Material Density support and [the NavigationRail](https://api.flutter.dev/flutter/material/NavigationRail-class.html), and experiments with deep integration into the underlying desktop OS with experiments in [Dart:FFI](https://dart.dev/guides/libraries/c-interop) and access to the system menu bar and standard dialogs. All of this work was to ensure that in addition to being suitable for mobile-style experiences, Flutter is ready to handle full-featured, full-sized desktop apps. + +It has long been our vision for Flutter to power platforms. We’ve seen this manifest already at Google with products like [the Assistant](https://developers.googleblog.com/2019/05/Flutter-io19.html), so now we’re thrilled to see others harnessing Flutter to power more platforms. Today we are happy to jointly announce the availability of the Linux alpha for Flutter alongside Canonical, the publisher of [Ubuntu](https://ubuntu.com/), the world’s most popular desktop Linux distribution. + +## Why Flutter for Linux? + +Last year, when Google announced desktop-class application support with Flutter, Canonical saw an exciting opportunity to make Linux distributions, including Ubuntu, an attractive target platform for Flutter app developers. Flutter’s native cross-platform story is growing rapidly and Canonical wanted to be at the vanguard. By enabling desktop Linux support in Flutter, Canonical is making it very easy for application developers to publish their apps for Linux users via the Snap Store, the app store for Linux. By making Linux a first-class Flutter platform, Canonical is inviting application developers to publish their apps to millions of Linux users and broaden the availability of high quality applications available to them. + +A number of things about Flutter were exciting for Canonical: + +* Fast growing ecosystem of application developers + +* Multiple platform support + +* Highly optimized native applications + +* Modern UI framework supporting declarative, reactive, and composable widgets + +* Rich development platform using Visual Studio Code, Android Studio, and IntelliJ + +Google’s initial announcement announcement of Flutter desktop support started with an alpha release supporting macOS and plans for Linux and Windows. Canonical is making a significant investment in Flutter by dedicating a team of developers to work alongside Google’s developers to bring the best Flutter experience to the majority of Linux distributions. Canonical will continue to collaborate with Google to further improve Linux support and to maintain feature parity with the other supported platforms. + +## Flokk: Proving Flutter’s readiness for the desktop + +To demonstrate that Flutter was ready for the desktop, we worked with the designers and developers at [gskinner](http://gskinner.com) to create an innovative, beautiful Flutter desktop app. Flokk is a real-world app that works with real-world data, specifically your Google Contacts list. + +<YoutubeEmbed id="cTFJcq7UTRY" title="YouTube" fullwidth="true"/> + + +In addition to being able to manage your contacts, including searching contacts, adding new contacts, and editing existing contacts, Flokk also lets you associate GitHub and Twitter handle information with your contacts. + +<DashImage figure src="images/0uMT4fCQFO8VQwB0Q.webp" alt="The Flokk Contacts app was built with Flutter to target the desktop" caption="The Flokk Contacts app was built with Flutter to target the desktop" /> + + +The display of GitHub and Twitter notifications turns your contacts into your own personal social network. And if you’re not seeing your favorite social networks in Flokk Contacts, then the good news is that Flokk is [completely open source](https://github.com/gskinnerTeam/Flokk/), so you can submit a PR to add your favorite. + +In addition to innovating in the social space, Flokk uses Flutter features to look and feel great. As just one example, the dark theme not only switches the colors but animates the changes as it does so. + +<DashImage figure src="images/0dKv6teOS8d4zjiWk.webp" alt="Flutter enables Flokk to take advantage of smooth animation, high-performance scrolling, and easy theming" caption="Flutter enables Flokk to take advantage of smooth animation, high-performance scrolling, and easy theming" /> + + +The creative team behind the Flokk Contacts app was led by [Grant Skinner](http://gskinner.com), who’s well known for both excellence in design and implementation of innovative user experience. Grant had this to say about working with Flutter on Linux: +> *“Building the Flokk Contacts app was a breeze! We were able to apply all our previous expertise in Flutter to target Linux with virtually no adjustments, and the app runs fantastically. Working with the Canonical team was a wonderful experience; they were enthusiastic, engaged, and passionate about making Flutter better not just for Linux, but for every platform. It was an amazing project, and I’m thrilled to be able to target another major OS with Flutter.” — Grant Skinner* + +If you’d like to use the Flokk app on your Linux machine, you can download the latest release on [GitHub](https://github.com/gskinnerTeam/Flokk/releases). Or, if you’re running snapd, you can download the Flokk App from the [Snap Store](https://snapcraft.io/flokk-contacts). + +## Easy install of Flutter on Linux + +Now that you’ve seen how well Flutter works for desktop-class applications, especially on Linux, you’re going to want to get it running on your own Linux machine. To make that as simple as possible, we’re pleased to provide the Flutter SDK for Linux as a snap in the [Snap Store.](https://snapcraft.io/flutter) The Flutter SDK snap provides everything needed to develop Flutter apps on your favorite Linux distribution. No need to install a bunch of development dependencies; simply install the Flutter SDK snap and your favorite IDE, and you have everything you need to create, build, and publish your applications for Linux. + +For example, if you’d like to get started developing Flutter applications for Linux and your IDE of choice is Visual Studio Code, this is all you need to do at your Linux terminal: + +``` +$ snap install --classic flutter +$ snap install --classic code +$ code --install-extension dart-code.flutter +``` + + +If you also want to use Linux to develop mobile apps, you can do so by [installing the Android SDK or Android Studio](https://developer.android.com/studio/install#linux) (which includes the Android SDK). For more information on the Flutter SDK as a snap, see [https://snapcraft.io/flutter](https://snapcraft.io/flutter) + +## Flutter for Linux desktop + +Once the Flutter SDK is installed on your Linux machine, to build a desktop app you need to upgrade to the Flutter dev or master channel. Then enable Linux desktop support: + +``` +$ flutter channel dev +$ flutter upgrade +$ flutter config --enable-linux-desktop +``` + + +Now when you create a new Flutter project, you’ll get a `linux` subdirectory that lets you run the app on your Linux desktop: + +``` +$ flutter create counter +$ cd counter +$ flutter run -d linux +``` + + +<DashImage figure src="images/0isPy4BXVD5J5hyQg.webp" alt="The famous Flutter Counter app runs great on Linux, too" caption="The famous Flutter Counter app runs great on Linux, too" /> + + +What you’ll get is a shiny new Linux app built with Flutter running on the latest stable version of GTK+. If you have an existing Flutter project to which you’d like to add Linux support after you have Linux enabled, you can add the `linux` subdirectory like so: + +``` +$ cd my_flutter_app +$ flutter create . +``` + + +This will create the `linux` subdirectory with the Runner project that you need to build and run your Flutter app on the Linux desktop. + +## Accessing native code in Linux from Flutter + +In addition to supporting the desktop by writing Dart to create Flutter widgets, your Linux desktop apps can also access all of native Linux using [platform channels](https://flutter.dev/docs/development/platform-integration/platform-channels) or [the Dart Foreign Function Interface for C/C++](https://dart.dev/guides/libraries/c-interop). Or, if you’d like to reuse pre-existing code, you can [find that code on pub.dev](http://pub.dev/flutter/packages?platform=linux), Dart and Flutter’s package manager site. Most of the packages you’ll find on pub.dev are pure Dart, most of which work just fine in Linux apps. Some packages, called plugins, have native code in them that are specific to one or more platforms. As part of this release, we’ve published three plugins on pub.dev that use native functionality of Linux: + +* [url_launcher](https://pub.dev/packages/url_launcher): launch the default browser at a URL provided + +* [shared_preferences](https://pub.dev/packages/shared_preferences): user preferences shared between app sessions + +* [path_provider](https://pub.dev/packages/path_provider): path information about special-purpose directories, such as downloads, pictures, and so on + +Each of these plugins is available for you to use in your apps, as well as serving as an example of how to access Linux natively from your Flutter code, like [the Linux implementation of url_launcher](https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_linux/). + +## Deploying to the Snap Store + +To deploy your Flutter app to the Snap Store, you first need to install Snapcraft, the tool that you’ll use to build and publish your application as a snap: + +``` +$ sudo snap install snapcraft --classic +``` + + +To drive the Snapcraft tool, you’ll need to create a `snapcraft.yaml` file in your app’s project directory. As one example, this is the `snapcraft.yaml` file for Flokk: + +``` +name: flokk-contacts +version: 1.0.1 +summary: Flokk Contacts +description: A fresh and modern Google Contacts manager that integrates with GitHub and Twitter. +confinement: strict +base: core18 +grade: stable + +apps: + flokk-contacts: + command: flokk-contacts + extensions: [flutter-master] + plugs: + - network + +parts: + flokk-contacts: + source: . + plugin: flutter + flutter-target: lib/main.dart # app's main entry-point file +``` + + +In the directory with your `snapcraft.yaml` file, you can now run `snapcraft` to build a snap of your application. + +``` +$ snapcraft +``` + + +If all goes well, this produces a file in your current working directory like `flokk-contacts_1.0.1_amd64.snap`. + +Once you’ve [set up your account](https://snapcraft.io/docs/creating-your-developer-account) for publishing in the Snap Store, you can publish your snap: + +``` +$ snapcraft login +$ snapcraft register flokk-contacts +$ snapcraft upload flokk-contacts_1.0.1_amd64.snap --release edge +``` + + +This command will upload the application to the Snap Store and attempt to publish it to the [edge channel](https://snapcraft.io/docs/channels#heading--risk-levels). Once your application is published in the edge channel, it can be installed either via the Snap Store Desktop client or using the command line: + +``` +$ snap install --edge flokk-contacts +``` + + +For more details on building your first snap and publishing it in the Snap Store, see [https://snapcraft.io/first-snap#flutter](https://snapcraft.io/first-snap#flutter) for a guided tutorial. + +## Flutter Linux desktop samples + +The Flokk app is an excellent example of a real-world Flutter app targeting the Linux desktop. For a simpler sample, you can check out [the Photo Search app](https://github.com/flutter/samples/tree/master/experimental/desktop_photo_search), which was also built specifically to show off desktop features. + +<DashImage figure src="images/0y2IpiLOI01i9BqiC.webp" alt="Try the Photo Search sample on Linux" caption="Try the Photo Search sample on Linux" /> + + +Photo Search is a simple online photo search app that uses several plugins to access native platform functionality, supporting both macOS and Linux. + +For an example Linux desktop app with step-by-step instructions, I recommend [the Write a Flutter desktop application codelab](https://codelabs.developers.google.com/codelabs/flutter-github-graphql-client/index.html#0), which walks you through using OAuth and GraphQL to build a GitHub client in Flutter. + +<DashImage figure src="images/0vktUouQPnTpgk0Ko.webp" alt="The Flutter GitHub codelab client application in action" caption="The Flutter GitHub codelab client application in action" /> + + +For a more comprehensive app that exercises much more of the surface area of Flutter as well as providing several little applets, I recommend [the Flutter Gallery](https://github.com/flutter/gallery), which was redesigned last year to support desktop as well as mobile. If you’d like to see that in action, you can check that out [on the Snap Store](https://snapcraft.io/flutter-gallery), too. + +<DashImage figure src="images/0yW96xpHMveP4-kUm.webp" alt="The Flutter Gallery sample is available on the Snap Store" caption="The Flutter Gallery sample is available on the Snap Store" /> + + +One more desktop app that shows off the fun side of Flutter is a multi-player game built by Thorsten Lorenz called [batufo](https://thlorenz.com/batufo/). Set against a beautiful backdrop, the game lets players from all over the world compete against each other in real time. + +<DashImage figure src="images/1ghwj9i6N9nvBvNmKdP8oZw.gif" alt="Play in real time across multiple Flutter platforms" caption="Play in real time across multiple Flutter platforms" /> + + +Thorsten has been building this game to support multiple Flutter platforms including Linux, macOS, Android, and iOS. If you’d like to see how he did it and follow along with future updates, he makes his coding sessions available as [videos](https://thlorenz.com/batufo/#watch) and [the code available on GitHub](https://github.com/thlorenz/batufo). To install the game from Linux, you can pull it down from the [Snap Store](https://snapcraft.io/batufo). + +## Summary + +With this alpha release and the close partnership between Google and Canonical, Linux developers get Flutter support for their operating system of choice. Install the [Flutter SDK via snap](https://snapcraft.io/flutter). Build and test your desktop app on Linux using Visual Studio Code or Android Studio. Deploy your app to the Snap Store. For the latest details, see [the desktop page on flutter.dev](http://flutter.dev/desktop). And above all else, [make sure to provide feedback](https://github.com/flutter/flutter/issues) so that we can continue making Flutter the best it can be on Linux, as we strive to do for every supported Flutter platform. + +Flutter for Linux from the Canonical team is a giant step forward for our dream of making Flutter the best way to build an app, no matter which platform you’re targeting. Targeting the desktop has made the Flutter engine that much more adaptable to a long tail of devices that Google itself can’t support directly, but for which we plan to continue to build partnerships and to enable the ecosystem. + +Wherever there are devices that need fast, beautiful apps, that’s where we want Flutter to be. \ No newline at end of file diff --git a/sites/www/content/blog/announcing-flutter-release-preview-1/images/0-1m66Jxnec3s8Qnb.webp b/sites/www/content/blog/announcing-flutter-release-preview-1/images/0-1m66Jxnec3s8Qnb.webp new file mode 100644 index 0000000000..e2a37ac271 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-release-preview-1/images/0-1m66Jxnec3s8Qnb.webp differ diff --git a/sites/www/content/blog/announcing-flutter-release-preview-1/images/19g1lJx2vzCJ2mdMKCaHsnA.webp b/sites/www/content/blog/announcing-flutter-release-preview-1/images/19g1lJx2vzCJ2mdMKCaHsnA.webp new file mode 100644 index 0000000000..5082c33ac4 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-release-preview-1/images/19g1lJx2vzCJ2mdMKCaHsnA.webp differ diff --git a/sites/www/content/blog/announcing-flutter-release-preview-1/images/1LZ8v_AJHi69RCnxq7EeftQ.webp b/sites/www/content/blog/announcing-flutter-release-preview-1/images/1LZ8v_AJHi69RCnxq7EeftQ.webp new file mode 100644 index 0000000000..ce53031054 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-release-preview-1/images/1LZ8v_AJHi69RCnxq7EeftQ.webp differ diff --git a/sites/www/content/blog/announcing-flutter-release-preview-1/images/1PEcgvfvQj4Gd1hFBBfCX_g.webp b/sites/www/content/blog/announcing-flutter-release-preview-1/images/1PEcgvfvQj4Gd1hFBBfCX_g.webp new file mode 100644 index 0000000000..ed246afdef Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-release-preview-1/images/1PEcgvfvQj4Gd1hFBBfCX_g.webp differ diff --git a/sites/www/content/blog/announcing-flutter-release-preview-1/images/1bE4ebdAs3EnxgqV2m2y_7g.webp b/sites/www/content/blog/announcing-flutter-release-preview-1/images/1bE4ebdAs3EnxgqV2m2y_7g.webp new file mode 100644 index 0000000000..2f33335e54 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-release-preview-1/images/1bE4ebdAs3EnxgqV2m2y_7g.webp differ diff --git a/sites/www/content/blog/announcing-flutter-release-preview-1/index.md b/sites/www/content/blog/announcing-flutter-release-preview-1/index.md new file mode 100644 index 0000000000..02f1b8eee4 --- /dev/null +++ b/sites/www/content/blog/announcing-flutter-release-preview-1/index.md @@ -0,0 +1,91 @@ +--- +title: "Announcing Flutter Release Preview 1" +description: "Community momentum builds on the path to release" +publishDate: 2018-06-20 +author: timsneath +image: images/1PEcgvfvQj4Gd1hFBBfCX_g.webp +category: releases +layout: blog +--- + +Today, we’re delighted to announce **Flutter Release Preview 1**, signaling a new phase of development for Flutter as we move into the final stages of stabilization for 1.0. + +## Flutter progress since Google I/O + +Google I/O last month was something of a celebration for the Flutter team: having reached beta, it was good to meet with many developers who are learning, prototyping, or building with Flutter. Through the various technical sessions, we covered topics like [UI design with Flutter and Material](https://www.youtube.com/watch?v=hA0hrpR-o8U), [mobile development with Flutter and Firebase](https://www.youtube.com/watch?v=p4yLzYwy_4g), and [architectural practices for complex Flutter apps](https://www.youtube.com/watch?v=RS36gBEp8OI). + +It was also good to showcase some of the [early apps that are using Flutter](https://flutter.io/showcase), as this video highlight reel demonstrates: + +<YoutubeEmbed id="ul7IbPvWBwc" title="Flutter at I/O: Sandbox Highlight Reel" fullwidth="true"/> + + +In the aftermath of Google I/O, we continue to see rapid growth in the Flutter ecosystem, with a **50% increase in active Flutter users**. + +One common measure of popularity and momentum is the number of GitHub stars, and we’ve also seen growth here, with Flutter recently becoming one of the [top 100 software repos on GitHub](https://github.com/timsneath/github-tracker). Indeed, even compared to many other popular open source projects, Flutter has been growing rapidly since the first beta in February, as shown in this graph ([source](https://github.com/timsneath/r-github-stars/blob/master/flutterStars.R)): + +<DashImage figure src="images/1bE4ebdAs3EnxgqV2m2y_7g.webp" /> + + +In the weeks following I/O, we have been greatly encouraged by the number of user groups, meetups, and events that have sprung up all over the world. We’ve seen over 150 individual Flutter events taking place across fifty countries: from [New York City](https://www.meetup.com/flutter-nyc/?_cookie-check=ovV9o45C3PiCO2ts) to [Uyo, Nigeria](https://twitter.com/Esidemsam/status/1000023177081344000); from [Tokyo and Osaka in Japan](https://flutter-jp.connpass.com/) to [Nuremberg, Germany](https://twitter.com/search?q=flutter%20extended&src=typd). + +<DashImage figure src="images/0-1m66Jxnec3s8Qnb.webp" alt="Flutter events happening around the world" caption="Flutter events happening around the world" /> + + +In addition to these worldwide community events, we’re also starting to see early adopters of Flutter publish their work to the Apple and Google stores. At Google I/O, we highlighted some [apps and endorsements such as Alibaba, Groupon, and Google AdWords](http://flutter.io/showcase); it’s since been fun to see both larger and smaller-scale apps starting to appear from the community. Here are a few diverse examples from around the world: + +* Suez Canal Authority (Egypt): [Android](https://play.google.com/store/apps/details?id=com.suezcanal.obour), [iOS](https://itunes.apple.com/us/app/elnasr-bridge/id1347983514?mt=8) + +* CARTUNE (Japan): [Android](https://play.google.com/store/apps/details?id=me.cartune.android) + +* Reflectly (Denmark): [Android](https://play.google.com/store/apps/details?id=com.reflectlyApp) + +* Tengwar (Middle Earth Elvish script) transcriber (Germany): [Android](https://play.google.com/store/apps/details?id=com.woalk.apps.tengwar.de), [iOS](https://itunes.apple.com/us/app/tengwar-umschreiber/id1367958873?mt=8) + +* Beer Me Up (France) — [Android](https://play.google.com/store/apps/details?id=com.benoitletondor.beermeup), [iOS](https://itunes.apple.com/us/app/beer-me-up-your-beer-logging/id1393867499) + +## Announcing Flutter Release Preview 1 + +Today we’re taking another big step forward, with the immediate availability of [Flutter Release Preview 1](https://flutter.io/). + +<DashImage figure src="images/1PEcgvfvQj4Gd1hFBBfCX_g.webp" /> + + +The shift from *beta* to *release preview* signals our confidence in the stability and quality of what we have, and our focus on bug fixing and stabilization. + +We’re making this announcement during the keynote of the [GMTC Global Front-End Conference in Beijing, China](https://gmtc.geekbang.org/), a gathering of around a thousand front-end and mobile developers sponsored by GK Tech and InfoQ China. China is a logical venue for this announcement, as we’ve seen particularly strong adoption for Flutter by leading companies there. In particular, Alibaba has now deployed their Flutter-based app to millions of devices, and Tencent is currently rolling out their NOW app with Flutter. + +We are encouraged by the growing number of contributions we’re seeing from the broader community. External contributions to Release Preview 1 include Flutter support for [hardware keyboard and barcode scanners](https://github.com/flutter/flutter/issues/11177), [video recording](https://github.com/flutter/plugins/pull/458), and including a [folder of image or font assets](https://github.com/flutter/flutter/issues/4890). In addition, we’re seeing many new packages contributed to the [package site](https://pub.dartlang.org/flutter), such as [Flutter Platform Widgets](https://pub.dartlang.org/packages/flutter_platform_widgets), a set of widgets that automatically adapt for iOS or Android; [mlkit](https://pub.dartlang.org/packages/mlkit), a wrapper for the Firebase MLKit APIs; and [Sequence Animation](https://pub.dartlang.org/packages/flutter_sequence_animation), which helps with constructing a series of connected animations. + +As for the Flutter team, our focus has shifted slightly towards scenario completeness. We’ve continued to improve the [video player package](https://pub.dartlang.org/packages/video_player#-changelog-tab-), with broader format support and reliability improvements. We’ve further extended Firebase support to include [Firebase Dynamic Links](https://pub.dartlang.org/packages/firebase_dynamic_links#-readme-tab-), an app solution for creating and handling links across multiple platforms. We’ve broadened support to include [32-bit iOS devices](https://github.com/flutter/engine/wiki/iOS-Builds-Supporting-ARMv7) with ARMv7 chips, enabling apps written with Flutter to run on older devices (like the iPad Mini and iPhone 5c) that remain popular across the world. + +Lastly, we’ve added experimental instructions on [adding Flutter widgets to an existing Android or iOS app](https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps). While some iOS and Android apps in production already use this approach, we are working on improving our documentation and tooling to make it much easier to develop a mixed app. Keep an eye on [this wiki article](https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps) to follow our progress. + +How do you [upgrade](https://flutter.io/upgrading/) to Flutter Release Preview 1? If you’re on the beta channel already, it just takes one command: + +``` +$ flutter upgrade +``` + + +### Tooling for Flutter Release Preview 1 + +We continue to make significant improvements to Flutter tools, with a new update of the [Flutter extension for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter) adding a new outline view, statement completion, and the ability to launch emulators directly from Visual Studio Code. + +<DashImage figure src="images/19g1lJx2vzCJ2mdMKCaHsnA.webp" alt="Flutter Studio, a web-based community tool for building Flutter screens" caption="Flutter Studio, a web-based community tool for building Flutter screens" /> + + +Google is not the only organization working on Flutter tooling. One community effort that shows early promise is [Flutter Studio](https://flutterstudio.app/), a web-based tool for creating Flutter-based user interfaces. The tool has property editors for each of the 50+ Flutter widgets it supports, so it’s a good way to explore the widget catalog. The tool itself is written in [Dart](https://dartlang.org) — the same language used to build Flutter apps. You can read more about how it was built on [the developer’s blog](https://medium.com/@pmutisya/flutter-studio-version-2-41cce10fcf3d), where he also talks about his positive experiences with building a large web app with Dart. + +## Flutter in China + +China has the third largest population of developers using Flutter, after the USA and India. We are hugely appreciative of the local developers who have helped us with China-based mirrors of the pub package site and community-maintained translations of the code labs. A [Flutter in China website](http://flutter-io.cn) is now available — launched jointly with the community to help developers in China build their skills. + +<DashImage figure src="images/1LZ8v_AJHi69RCnxq7EeftQ.webp" alt="Flutter in China website" caption="Flutter in China website" /> + + +## Conclusion + +It’s been fun to watch others encounter Flutter for the first time. [This article from an iOS developer](https://medium.com/@seenickcode/what-it-was-like-to-write-a-full-blown-flutter-app-330d8202825b) who has recently completed porting an iOS app to Flutter is a positive endorsement of the project’s readiness for real-world production usage: +> “I haven’t been this excited about a technology since Ruby on Rails or Go… After dedicating years to learning iOS app dev in-depth, it killed me that I was alienating so many Android friends out there. Also, learning other cross platform frameworks at the time was super unattractive to me because of what was available… Writing a Flutter app has been a litmus test and Flutter passed the test. Flutter is something I feel like I can really invest in and most importantly, really enjoy using.” + +As we get ever closer to publishing our first release from the “stable” channel, we’re ready for more developers to build and deploy solutions that use this Release Preview. There are plenty of training offerings to help you learn Flutter: from [I/O sessions](https://www.youtube.com/playlist?list=PL0o58xSX-Jvi6zBDe7SW4W6h5hauQ-xxF) to [newsletters](https://flutterweekly.net/) to [hands-on videos](https://www.youtube.com/watch?v=syd0c9Vi2hg) to [developer shows](https://www.youtube.com/watch?v=yr8F2S3Amas). And we have [discussion groups](https://groups.google.com/forum/#!forum/flutter-dev), [chat rooms](https://gitter.im/flutter/flutter), and [community support](https://stackoverflow.com/questions/tagged/flutter) available to get you through any stumbling blocks you may encounter. We’re ready for you! \ No newline at end of file diff --git a/sites/www/content/blog/announcing-flutter-windows-alpha/images/039MKtw4ICepbL_8d.webp b/sites/www/content/blog/announcing-flutter-windows-alpha/images/039MKtw4ICepbL_8d.webp new file mode 100644 index 0000000000..49a958a27a Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-windows-alpha/images/039MKtw4ICepbL_8d.webp differ diff --git a/sites/www/content/blog/announcing-flutter-windows-alpha/images/03OlPPAMAvO84mJ6M.webp b/sites/www/content/blog/announcing-flutter-windows-alpha/images/03OlPPAMAvO84mJ6M.webp new file mode 100644 index 0000000000..a966cdd4e8 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-windows-alpha/images/03OlPPAMAvO84mJ6M.webp differ diff --git a/sites/www/content/blog/announcing-flutter-windows-alpha/images/0N_tKeqs435R4yICO.webp b/sites/www/content/blog/announcing-flutter-windows-alpha/images/0N_tKeqs435R4yICO.webp new file mode 100644 index 0000000000..c2aee98f22 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-windows-alpha/images/0N_tKeqs435R4yICO.webp differ diff --git a/sites/www/content/blog/announcing-flutter-windows-alpha/images/0O-akivVSsv0tL0wI.webp b/sites/www/content/blog/announcing-flutter-windows-alpha/images/0O-akivVSsv0tL0wI.webp new file mode 100644 index 0000000000..9f3be7d445 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-windows-alpha/images/0O-akivVSsv0tL0wI.webp differ diff --git a/sites/www/content/blog/announcing-flutter-windows-alpha/images/0ZuwlwG4e6fwCeQ_1.webp b/sites/www/content/blog/announcing-flutter-windows-alpha/images/0ZuwlwG4e6fwCeQ_1.webp new file mode 100644 index 0000000000..5fc031f2ec Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-windows-alpha/images/0ZuwlwG4e6fwCeQ_1.webp differ diff --git a/sites/www/content/blog/announcing-flutter-windows-alpha/images/0cNcLDSm8fwlwux15.webp b/sites/www/content/blog/announcing-flutter-windows-alpha/images/0cNcLDSm8fwlwux15.webp new file mode 100644 index 0000000000..410316bf39 Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-windows-alpha/images/0cNcLDSm8fwlwux15.webp differ diff --git a/sites/www/content/blog/announcing-flutter-windows-alpha/images/0dY1TC_i0KyjKNrvh.webp b/sites/www/content/blog/announcing-flutter-windows-alpha/images/0dY1TC_i0KyjKNrvh.webp new file mode 100644 index 0000000000..ba297d4c6f Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-windows-alpha/images/0dY1TC_i0KyjKNrvh.webp differ diff --git a/sites/www/content/blog/announcing-flutter-windows-alpha/images/1ZIxhg4Lx_KI5v4bc1zi5zg.webp b/sites/www/content/blog/announcing-flutter-windows-alpha/images/1ZIxhg4Lx_KI5v4bc1zi5zg.webp new file mode 100644 index 0000000000..c0ff99e60e Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-windows-alpha/images/1ZIxhg4Lx_KI5v4bc1zi5zg.webp differ diff --git a/sites/www/content/blog/announcing-flutter-windows-alpha/images/1fsnlPFkpQWZjYIv-NCE6Vg.webp b/sites/www/content/blog/announcing-flutter-windows-alpha/images/1fsnlPFkpQWZjYIv-NCE6Vg.webp new file mode 100644 index 0000000000..44c6e21dfb Binary files /dev/null and b/sites/www/content/blog/announcing-flutter-windows-alpha/images/1fsnlPFkpQWZjYIv-NCE6Vg.webp differ diff --git a/sites/www/content/blog/announcing-flutter-windows-alpha/index.md b/sites/www/content/blog/announcing-flutter-windows-alpha/index.md new file mode 100644 index 0000000000..95a2492d16 --- /dev/null +++ b/sites/www/content/blog/announcing-flutter-windows-alpha/index.md @@ -0,0 +1,208 @@ +--- +title: "Announcing Flutter Windows Alpha" +description: "Native Windows app compilation, some early plugins, and a desktop-ready Flutter Gallery app" +publishDate: 2020-09-23 +author: csells +image: images/039MKtw4ICepbL_8d.webp +category: announcements +layout: blog +--- + +Our mission is to provide developers with an open source, high-productivity framework for building beautiful, native apps on any platform. So far, we’ve shipped production-quality support for Android and iOS, with eight stable releases and over 100,000 apps shipped to the Google Play Store alone. We continue to broaden our focus to include other platforms including web, macOS, and Linux. Today, we’re pleased to announce an additional target for Flutter with the alpha release of Flutter support for Windows. + +Windows remains a popular choice for desktop and laptop devices, with Microsoft reporting over [one billion active devices running Windows 10](https://blogs.windows.com/windowsexperience/2020/03/16/windows-10-powering-the-world-with-1-billion-monthly-active-devices/). Our own statistics show that over half of all Flutter developers use Windows, so it’s a natural target for Flutter. Native desktop support opens up a variety of exciting possibilities for Flutter, including improved developer tooling, reduced friction for new users, and of course apps that can reach any device a user might have from a single codebase. + +## Adding Windows to Flutter + +As described in our [architectural overview](https://flutter.dev/docs/resources/architectural-overview), Flutter is a cross-platform UI toolkit that is designed to allow code reuse across operating systems such as iOS and Android, while also allowing applications to interface directly with underlying platform services. The goal is to enable developers to deliver high-performance apps that feel natural on different platforms, embracing differences where they exist while sharing as much code as possible. At the core of Flutter is the engine, which supports the primitives necessary to support all Flutter applications. The engine is responsible for rasterizing composited scenes whenever a new frame needs to be painted. It provides the low-level implementation of Flutter’s core API, including graphics, text layout, file and network I/O, accessibility support, plugin architecture, and a Dart runtime and compile toolchain. + +Each new platform we add to Flutter expands the core framework with new services to enable it to shine on that platform. We started on Android and iOS with Material Design as well as a touch-based, mobile-centric user interface that is designed to be pixel-perfect on both mobile platforms. Adding support for desktop form factors with web, Windows, macOS, and Linux brings a whole new set of services, including robust support for keyboards, mice, mouse wheels and controllers on the input side as well as widgets that adapt or even work best at the larger screen sizes that come with web and desktop apps. + +Furthermore, each new platform doesn’t just influence the Flutter framework and engine, but a lot of other things as well: + +* **Toolchain updates:** adding a new target to the CLI and IDE tools (in this case Windows) + +* **Shell:** support for handling input from Windows via `WM_*` messages and output via [ANGLE](https://opensource.google/projects/angle), which uses [Skia](https://skia.org/) to render at native speed to an underlying DirectX surface + +* **Runner:** every project gets a shell application for the supported targets. For Windows, it’s a Win32/C++ program that loads your Flutter code and executes it at runtime. It’s a good place to add native code to your app if you need it. + +* **Plugins:** A plugin is a mixture of Dart code and native code for each of the platforms that the plugin supports. That native code needs to be added for each plugin that is compiled into your Flutter app on Windows. + +This alpha release offers a solid foundation that we’ll stabilize over the coming months. With support for Windows 7 and above, we hope this gives adventurous developers something to get started with. + +## Exploring some sample apps + +To see Flutter support for Windows in action, you might want to try out some of the sample apps that we’ve created that run well on Windows with our newly-added support. The first, the Flokk app, was created in concert with the designers and developers at [gskinner.com](http://gskinner.com). The goal was to demonstrate that Flutter was ready for the desktop by creating an innovative, beautiful Flutter desktop app. Flokk is an app that works with your real Google Contacts data as well as showing your contacts’ activity on GitHub and Twitter. + +<DashImage figure src="images/039MKtw4ICepbL_8d.webp" /> + + +If you’d like to use the Flokk app on your Windows machine, you can [download the latest release on GitHub](https://github.com/gskinnerTeam/Flokk/releases). And if you’d like to read about how gskinner built this app, see their excellent blog post: [Flokk — How we built a Desktop App Using Flutter](https://blog.gskinner.com/archives/2020/09/flokk---how-we-built-a-desktop-app-using-flutter.html). + +In addition, the Flutter Gallery app, our showcase app for all things Flutter, was completely rewritten recently to add support for desktop form factors. This allowed us to check that it works well on [the web](https://gallery.flutter.dev) as well as Windows, macOS, and Linux. + +<DashImage figure src="images/0N_tKeqs435R4yICO.webp" /> + + +Many studies in the Gallery demonstrate ideas for different app styles that we recommend when designing your own Windows app with Flutter. When you find something you like, [the code is available on GitHub](https://github.com/flutter/gallery). + +## Getting started with Flutter for Windows + +Get started by installing the Flutter SDK according to the [Windows install instructions](https://flutter.dev/docs/get-started/install/windows). To target Windows desktop, you first need to [install the tooling described in the desktop docs](https://flutter.dev/desktop#additional-windows-requirements). By default, Flutter assumes that you’re building production software and isn’t configured to develop Windows apps. However, that’s easily fixed from the command line: + +``` +$ flutter channel dev +$ flutter upgrade +$ flutter config --enable-windows-desktop +``` + + +The first command sets Flutter to use the experimental-quality “dev” channel (instead of the “stable” channel, which is the default). This allows you to use platform support that’s still in alpha, like Windows. The second command pulls down the latest bits on that channel. The third command enables Windows app development on your PC. + +Once you’ve set it up, every time you create a new Flutter app, using the extension support for either [Android Studio](https://flutter.dev/docs/get-started/editor?tab=androidstudio) or [Visual Studio Code](https://flutter.dev/docs/get-started/editor?tab=vscode), or from the command line, it creates a windows subfolder. + +<DashImage figure src="images/0O-akivVSsv0tL0wI.webp" /> + + +If you’re curious, running the default app on Windows looks like the following: + +<DashImage figure src="images/1fsnlPFkpQWZjYIv-NCE6Vg.webp" /> + + +And finally, once you’ve created your app, building it creates a release-mode, native EXE file as well as the necessary supporting DLLs. At that point, if you want to experiment with running your new Windows app on any Windows 10 machine, even those that don’t have Flutter installed, you can follow [the steps to zip up the necessary files](https://flutter.dev/desktop#distribution) and go. + +## Plugins for Windows + +Even though we’ve just reached the alpha release, the Flutter community has already been working on plugins for Windows. Here are a few: + +* [url_launcher](https://pub.dev/packages/url_launcher): launch URLs in the browser from your app + +* [path_provider](https://pub.dev/packages/path_provider): find the path to special directions on the user’s machine like Documents or temp + +* [shared_preferences](https://pub.dev/packages/shared_preferences): keep user preferences serialized on disk between sessions of your app + +* [biometric_storage](https://pub.dev/packages/biometric_storage): storage encrypted by biometrics + +* [flutter_audio_desktop](https://pub.dev/packages/flutter_audio_desktop): play audio from your desktop apps + +The benefit of using these plugins is that most of them also support other Flutter platforms, which enables you to target your apps at Android, iOS, web, etc. as well as Windows. Furthermore, while about one-third of the available packages on [pub.dev](http://pub.dev) (the package manager for Dart and Flutter) are plugins with platform-specific code, most are not. For instance, [many of the highest quality and most used packages](https://pub.dev/flutter/favorites) are part of the Flutter Favorite program and most of them work on Windows. If you’d like to see the full list of packages that run on Windows, you can [run this query on pub.dev](https://pub.dev/flutter/packages?platform=windows). + +## Interop with Windows + +If you’d like to build your own plugins for Windows, you can. Once you’re on the dev channel and you have Windows enabled for your machine, you can get started with the following command: + +``` +$ flutter create --template plugin --platforms windows hello_plugin +``` + + +At that point, you’ll be able to add your Flutter code to the `lib` subfolder and your Windows code to the `windows` subfolder in your plugin project. You’ll communicate between the two stacks using [Platform Channels](https://flutter.dev/docs/development/platform-integration/platform-channels), which is essentially message passing between your Dart and C++ code. For a well crafted example of this, [see the url_launcher implementation](https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_windows). + +However, Platform Channels are not your only option for interop with Windows. If you like, you can use the [Dart FFI (Foreign Function Interface)](https://flutter.dev/docs/development/platform-integration/c-interop) to load libraries and call into C-style APIs, such as the Win32 API. Unlike url_launcher, which uses Platform Channels, the path_provider plugin was implemented using FFI, as you can see in [the GitHub repo](https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_windows). Instead of going back and forth between Dart and C++, FFI allows you to write code to import the API that you want directly. For example, here’s the code for calling the MessageBox API: + +```dart +typedef MessageBoxNative = Int32 Function( + IntPtr hWnd, + Pointer<Utf16> lpText, + Pointer<Utf16> lpCaption, + Int32 uType +); + +typedef MessageBoxDart = int Function( + int hWnd, + Pointer<Utf16> lpText, + Pointer<Utf16> lpCaption, + int uType +); + +final user32 = DynamicLibrary.open('user32.dll'); + +final win32MessageBox = + user32.lookupFunction<MessageBoxNative, MessageBoxDart>('MessageBoxW'); + +void showMessageBox(String message, String caption) => + win32MessageBox( + 0, // No owner window + Utf16.toUtf16(message), // Message + Utf16.toUtf16(caption), // Window title + 0 // OK button only + ); + +... + +// call just like any other Dart function +showMessageBox('Test Message', 'Window Caption'); +``` + +This code doesn’t incur the overhead of transitioning between two threads like Platform Channels. FFI includes support for many different kinds of APIs, including Win32, WinRT, and COM. But before you run off and wrap the entire C-based Windows API, please check out [the win32 plugin](https://pub.dev/packages/win32), which is already well on its way to doing just that. In fact, the [path_provider](https://pub.dev/packages/path_provider) plugin was itself implemented using the win32 plugin. For the details of how the win32 plugin was developed and how it works, check out the blog post, [Windows fun with Dart FFI](https://medium.com/@timsneath/windows-fun-with-dart-ffi-687c4619e78d). + +## Flutter for Windows resources + +Wherever you are in your Flutter for Windows journey, you should be sure to [read the desktop documentation on flutter.dev](http://flutter.dev/desktop), which includes the latest details. Also, you’ll want to take a spin through the Flutter [codelab for writing a Windows, macOS, and Windows desktop-targeted application](https://codelabs.developers.google.com/codelabs/flutter-github-graphql-client), which includes code for real-world scenarios like authentication using OAuth, accessing the GitHub API, and using GraphQL. Or, for another good example of Flutter desktop code that works on Windows, [check out the Photo Search sample](https://github.com/flutter/samples/tree/master/experimental/desktop_photo_search). + +<DashImage figure src="images/03OlPPAMAvO84mJ6M.webp" /> + + +It uses the standard Windows File Open dialog, a tree view widget, a splitter widget, and integrates the results with a real-world REST API. + +For other helpful desktop-oriented widgets, we recommend [the menubar plugin](https://github.com/google/flutter-desktop-embedding/tree/master/plugins/menubar), [the NavigationRail widget](https://api.flutter.dev/flutter/material/NavigationRail-class.html), and [the DataTable widget](https://api.flutter.dev/flutter/material/DataTable-class.html). You might also be interested in [the InteractiveViewer widget](https://api.flutter.dev/flutter/widgets/InteractiveViewer-class.html), which has full desktop support for mouse gestures to pan and zoom child widgets. + +Another useful set of widgets to explore are [those from SyncFusion](https://pub.dev/publishers/syncfusion.com/packages), who are already well-known in the Windows development community. They provide a wide range of enterprise-quality widgets for creating charts, gauges, data grids, and more. + +<DashImage figure src="images/0dY1TC_i0KyjKNrvh.webp" /> + + +These widgets have a community and enterprise license, so you can find the right fit for your project. + +## Flutter for Windows in the wild + +In addition to packages and plugins for Windows (and Flutter desktop in general), Flutter developers have also been building great apps to target Windows, like this experimental build from [Invoice Ninja](https://www.invoiceninja.com/): + +<DashImage figure src="images/0ZuwlwG4e6fwCeQ_1.webp" /> + + +Invoice Ninja is an invoicing company that depends on Flutter to bring in its revenue. They target Android and iOS in production today and have [a web-based demo](https://demo.invoiceninja.com) for you to try, but are also looking ahead to providing a desktop version. +> “At Invoice Ninja we’ve struggled in the past to support just web and mobile, at one point maintaining three separate codebases. With Flutter, and more recently Flutter Desktop, we’ve been able to build apps for every major platform with a single codebase. Not only do we basically get a free desktop version of our app but it has the best performance of them all!” +> — Hillel Coren, Co-Founder, Invoice Ninja + +If you’re interested in the implementation of a real-world, revenue-generating Flutter app that works on mobile and desktop form factors, [the source code is available on GitHub](https://github.com/invoiceninja/flutter-client). + +[Aartos](https://drone-detection-system.com/) is another company building great things, including a real-time drone detection system with [a multi-platform client written in Flutter](https://drone-detection-system.com/aartos-dds/product-overview/). Here’s an early version of the Windows client running right next to the mobile client: + +<YoutubeEmbed id="mGvPCT7Vc2Y" title="Flutter Client App for Aartos, Desktop + Mobile" fullwidth="true"/> + + +Both of these versions, targeting iOS and Windows, share the exact same codebase. + +If you’re an experienced Flutter developer and you find yourself switching between different versions of Flutter; for example, one version to ship your production mobile apps and another to experiment with the Windows alpha, then you might appreciate the Flutter Version Manager, which now comes with [a Windows GUI that you can download](https://github.com/leoafarias/fvm/releases). + +<YoutubeEmbed id="_WA71wSt2ww" title="FVM App for Windows" fullwidth="true"/> + + +This tool is open source, so can [see for yourself](https://github.com/leoafarias/fvm) how Leo makes it look so great. + +## What’s next + +Now that we’ve released the alpha, our attention shifts to completing the feature set and stabilizing the product for release. As an open source project, you can follow our [progress to beta on our GitHub site](https://github.com/flutter/flutter/projects/55), with remaining work to complete including accessibility, globalization and localization, enhanced keyboard and text handling, support for command line arguments, and more. + +In addition to supporting the classic Win32 API, we’re also experimenting with [a UWP-based version of the Flutter shell](https://docs.microsoft.com/en-us/windows/uwp/get-started/universal-application-platform-guide), which allows Flutter to reach an even broader variety of Windows-based devices, including Xbox. As part of that experiment, this week we released a UWP-based version of the [Flutter Gallery to the Windows Store](https://www.microsoft.com/en-us/p/flutter-gallery/9pdwctdfc7qq). + +The following screenshot shows the UWP-based Flutter Gallery running on an Xbox: + +<DashImage figure src="images/0cNcLDSm8fwlwux15.webp" /> + + +And here’s the same app running on a dual-screen Windows device running on the [Windows 10X emulator](https://docs.microsoft.com/en-us/dual-screen/windows/get-dev-tools): + +<DashImage figure src="images/1ZIxhg4Lx_KI5v4bc1zi5zg.webp" /> + + +[You can read more about the progress on Flutter for UWP on GitHub](https://github.com/flutter/flutter/issues/14967#issuecomment-697108439). + +## Summary + +With this release, we bring the power of Flutter to Windows, with its declarative, composable, reactive framework for developer productivity, its adaptable implementation of the Material spec so you can make your apps look and feel the way you want them to, as well as the full set of Flutter development and debugging tools. When complete, your app is compiled to native 64-bit code that you can package and bring to other Windows machines just like any other native app. And finally, you can use that same codebase to create an app targeting Android, iOS, web, macOS, and Linux. + +If you’d like to start building Windows apps with Flutter, we’d love [your feedback](https://github.com/flutter/flutter/issues)! If you prefer to use your Windows expertise to [build out the Windows implementation of popular plugins](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin), or build some Windows-centric tooling for Flutter (perhaps a CLI that creates MSIXs from the output of a `flutter build windows` command…), that’s also welcome! + +With the new support for Windows in Flutter, what are you going to build? \ No newline at end of file diff --git a/sites/www/content/blog/announcing-flutters-beta-2-release/images/14DxDp03lkhJN3engJhXhWQ.webp b/sites/www/content/blog/announcing-flutters-beta-2-release/images/14DxDp03lkhJN3engJhXhWQ.webp new file mode 100644 index 0000000000..b8f310cf0d Binary files /dev/null and b/sites/www/content/blog/announcing-flutters-beta-2-release/images/14DxDp03lkhJN3engJhXhWQ.webp differ diff --git a/sites/www/content/blog/announcing-flutters-beta-2-release/images/1hjgDPasw3bTtB9N54wxX2Q.webp b/sites/www/content/blog/announcing-flutters-beta-2-release/images/1hjgDPasw3bTtB9N54wxX2Q.webp new file mode 100644 index 0000000000..27b5497165 Binary files /dev/null and b/sites/www/content/blog/announcing-flutters-beta-2-release/images/1hjgDPasw3bTtB9N54wxX2Q.webp differ diff --git a/sites/www/content/blog/announcing-flutters-beta-2-release/index.md b/sites/www/content/blog/announcing-flutters-beta-2-release/index.md new file mode 100644 index 0000000000..3829308de2 --- /dev/null +++ b/sites/www/content/blog/announcing-flutters-beta-2-release/index.md @@ -0,0 +1,122 @@ +--- +title: "Announcing Flutter’s beta 2 release" +description: "We have a second beta build available of Flutter, Google’s new mobile UI framework that helps developers craft high-quality apps." +publishDate: 2018-04-09 +author: mit-mit +image: images/14DxDp03lkhJN3engJhXhWQ.webp +category: releases +layout: blog +--- + +## Announcing Flutter beta 2 + +We have a second beta build (v0.2.8) available of Flutter, Google’s new mobile UI framework that helps developers craft high-quality native interfaces for both iOS and Android. We’ve been working hard to continue to support Flutter’s rapidly expanding community and are now introducing easier installation, default support for Dart 2, and more. If you are a new to Flutter, [get started](https://flutter.io/get-started/?utm_source=google&utm_medium=blog&utm_campaign=beta_get_started) today with beta 2 at [flutter.io](https://flutter.io/?utm_source=google&utm_medium=blog&utm_campaign=beta_announcement). If you are an existing Flutter user please review the ‘Call to action’ below to ensure your code is compatible with Dart 2. + +<DashImage figure src="images/14DxDp03lkhJN3engJhXhWQ.webp" /> + + +## A view back at beta 1 + +We have been delighted to see the [strong reception](https://twitter.com/_eseidel/status/973641697962770432) since we launched [our first beta](https://medium.com/flutter-io/announcing-flutter-beta-1-build-beautiful-native-apps-dc142aea74c0) at Mobile World Congress in Barcelona in February. We saw a nice collection of posts in the general tech press (for example, [Ars Technica](https://arstechnica.com/gadgets/2018/02/google-starts-a-push-for-cross-platform-app-development-with-flutter-sdk/), [TechCrunch](https://techcrunch.com/2018/02/27/googles-flutter-ui-framework-is-now-in-beta/), [VentureBeat](https://venturebeat.com/2018/02/27/google-launches-flutter-beta-a-mobile-ui-framework-for-building-native-android-and-ios-apps/), [9to5Google](https://9to5google.com/2018/02/27/googles-flutter-sdk-now-available-beta/)), lots of new [Flutter posts on Medium](https://medium.com/tag/flutter/latest) by our awesome community, and a huge influx of new developers and organizations around the world downloading and using Flutter. + +## Improvements in our second beta + +### Easier installation + +We heard feedback that installation of Flutter could be easier; installing beta 1 required cloning our GitHub repo with git command line tools. So we reworked our installation procedure, and now support installing beta 2 by simply downloading and extracting an installation archive: [Windows](https://flutter.io/setup-windows/), [macOS](https://flutter.io/setup-macos/), and [Linux](https://flutter.io/setup-linux/). For Windows users, we also added a cool new Flutter Console so you can get started with Flutter commands right after downloading: + +<DashImage figure src="images/1hjgDPasw3bTtB9N54wxX2Q.webp" alt="Screenshot of the new Flutter Console for Windows" caption="Screenshot of the new Flutter Console for Windows" /> + + +### Improved developer tools + +Flutter’s developer tools and experience team shipped the [M23 tools milestone](https://groups.google.com/forum/#!topic/flutter-dev/E15CK8Eq8GM) featuring many improvements. Android Studio & IntelliJ gained a new ‘Outline view’ providing a structured tree view of the UI widgets in a build method, and support for ‘Format on Save’. VS Code gained support for running tests, multi project support, and a new picker to select the current Flutter SDK when several are installed. + +### Improved asset system + +We have done some pretty significant optimization of our asset system. We now place the assets using the structure that the underlying platform (Android, iOS) expects. This has a number of advantages. + +First, Flutter apps now launch faster as we don’t make unneeded extractions of the assets. The previous asset system occasionally caused slow launch times on older Android phones. + +Second, it enables plugins to access the assets from the native-code side of a plugin (i.e., from Java, Kotlin, Objective-C, or Swift). Let’s look at a concrete example, the [`video_player`](https://pub.dartlang.org/packages/video_player) plugin we launched a few months ago. Until now it was only capable of playing videos from the network, but several developers requested the ability to “pass” it video files that had been embedded into the app using Flutter’s asset system. With beta 2, and version 0.4.0 of the plugin, this is now possible. Because we place the assets as the underlying platform expects, they can be shared between Flutter and the native platform. And we provide new APIs ([Android](https://docs.flutter.io/javadoc/io/flutter/plugin/common/PluginRegistry.Registrar.html#lookupKeyForAsset-java.lang.String-), [iOS](https://docs.flutter.io/objcdoc/Protocols/FlutterPluginRegistrar.html#/c:objc(pl)FlutterPluginRegistrar(im)lookupKeyForAsset:)) for getting a suitable asset lookup key for use in the native code, for example the Android [AssetManager](https://developer.android.com/reference/android/content/res/AssetManager.html): + +```java +String assetName = call.argument("asset"); +String assetLookupKey = registrar.lookupKeyForAsset(assetName); +AssetFileDescriptor fd = registrar.context().getAssets().openFd(assetLookupKey); +player = new VideoPlayer(eventChannel, handle, fd, result); +``` + +### Dart 2 enabled by default + +Our first beta offered a preview of the Dart 2 programming language. Our testing has shown Dart 2 to be near complete, and very stable. Flutter’s second beta has Dart 2 enabled by default. As a result you will get [faster async](https://groups.google.com/forum/#!msg/flutter-dev/3R9qhjNGZk4/DJqZlep_AAAJ) calls, and a [much richer type system](https://www.dartlang.org/guides/language/sound-dart). + +As one example of how the new type system can save you a lot of debugging time, consider the following: + +```dart +void main() { + List todo = ['Eat', 'Sleep', new Task('Code Flutter', Priority.high), 'Profit']; + runApp( + new MaterialApp(home: new TodoList(todo)), + ); +} +class TodoList extends StatefulWidget { + final List<Widget> items; + TodoList(this.items); +} +``` + +The code has a bug. It’s passing a list of strings and tasks to a widget that expects a list of widgets. Static analysis does not catch this, because the programmer intentionally used lax static typing for the todo list (`List` is shorthand for `List<dynamic>`). + +Next consider how the app is using this list: + +```dart +Widget build(BuildContext context) { + List<Widget> remaining = widget.items.sublist(_done); + return new Scaffold( + body: (remaining.length <= 3) + ? new Column(children: remaining) + : new Text('${remaining.length} items left to do'), + floatingActionButton: new FloatingActionButton(onPressed: _doOneThing), + ); +} +``` + +Because access to the todo list items is conditional, the error kicks in late. In Flutter beta 1, there would be no error at startup, and only once the user has tapped the button would you be informed that a string cannot be used as a widget: + +```text +The following assertion was thrown building TodoList(dirty, state: TodoListState#fd618): +type 'String' is not a subtype of type 'Widget' of 'child' where + String is from dart:core + Widget is from package:flutter/src/widgets/framework.dart +``` + +With the new complete run-time checks in Dart 2, we prevent “errors waiting to happen” like this by instead failing early, at the point where your code makes a false claim about generic types. In this case, we fail as soon as the app launches, and the dynamic list `todo` is passed to the `TodoList` constructor, which expects a `List<Widget>`: + +```text +type 'List<dynamic>' is not a subtype of type 'List<Widget>' where + List is from dart:core + Object is from dart:core + List is from dart:core + Widget is from package:flutter/src/widgets/framework.dart + +main (file:///Users/mit/tmp/todo/lib/main.dart:4:37) +``` + +### Dart 2 and optional new/const + +Dart 2 also added support for making the new and const keywords optional when calling constructors, as discussed in our last beta blog post. During our final testing of beta 2 we found a number of [issues](https://github.com/dart-lang/sdk/issues/32798) so for now we recommend postponing any non-experimental use of optional new/const until we launch a new beta build declaring it ready. + +## Call to action: upgrading your code to Dart 2 + +As with all change, there is a cost: If you have existing Flutter code, you may have to upgrade some of it to be compatible with Dart 2. To begin upgrading, please make sure to [update the Flutter SDK to beta 2 (v0.2.8)](https://flutter.io/upgrading/), and your [Flutter IDE plugin](https://flutter.io/get-started/editor/) for Android Studio, IntelliJ, or VS Code to the latest release. + +Next, validate that your code passes static analysis (from the terminal, run `flutter analyze`, or use the problems view in [Android Studio/IntelliJ](https://flutter.io/using-ide/#editing-code-and-viewing-code-problems) or [VS Code](https://flutter.io/using-ide-vscode/#editing-code-and-viewing-issues)). Then, consult the comprehensive guidance in our [breaking changes post](https://groups.google.com/forum/#!topic/flutter-dev/H8dDhWg_c8I) for how to resolve issues you may see with the new, much more comprehensive runtime type checks in Dart 2. + +Should you need a bit more time to work through this, you can temporarily switch back to the old behavior with the [Dart 2 opt-out](https://github.com/flutter/flutter/wiki/Opting-out-of-Dart-2-with-Flutter-beta-2). This opt-out is expected to be supported for a single beta release only. + +## Closing thoughts + +We hope that you will enjoy our second beta, and feel our ongoing commitment to improving the Flutter product as we get to work on our next beta build. We really [welcome feedback](https://flutter.io/support/), and hope you will all share what you are working on; we truly enjoy seeing all the ways Flutter is being used to build great, beautiful apps! We are also excited to look ahead to next months Google I/O, where you can catch our [three sessions](https://events.google.com/io/schedule/?section=day&topic=flutter), experience our interactive sandbox area, and meet with us for great discussion! Oh, and before we leave, make sure to checkout the [great video](https://medium.com/flutter-io/video-hamilton-app-built-in-3-months-with-flutter-reaches-1m-installs-f37f81856be4) the Hamilton team published on their experience with Flutter. + +<YoutubeEmbed id="kfd-oLypqFI" title="Hamilton app built with Flutter and featured on iOS and Android (Flutter Developer Story)" fullwidth="true"/> diff --git a/sites/www/content/blog/announcing-general-availability-for-the-google-mobile-ads-sdk-for-flutter/images/0RZUZCrtJ7df_hnjI.webp b/sites/www/content/blog/announcing-general-availability-for-the-google-mobile-ads-sdk-for-flutter/images/0RZUZCrtJ7df_hnjI.webp new file mode 100644 index 0000000000..c0c4e83a27 Binary files /dev/null and b/sites/www/content/blog/announcing-general-availability-for-the-google-mobile-ads-sdk-for-flutter/images/0RZUZCrtJ7df_hnjI.webp differ diff --git a/sites/www/content/blog/announcing-general-availability-for-the-google-mobile-ads-sdk-for-flutter/images/0_i1bA47tKTTOaFAU.webp b/sites/www/content/blog/announcing-general-availability-for-the-google-mobile-ads-sdk-for-flutter/images/0_i1bA47tKTTOaFAU.webp new file mode 100644 index 0000000000..9d0423f56f Binary files /dev/null and b/sites/www/content/blog/announcing-general-availability-for-the-google-mobile-ads-sdk-for-flutter/images/0_i1bA47tKTTOaFAU.webp differ diff --git a/sites/www/content/blog/announcing-general-availability-for-the-google-mobile-ads-sdk-for-flutter/images/0zOegrJCYTuapgWgf.webp b/sites/www/content/blog/announcing-general-availability-for-the-google-mobile-ads-sdk-for-flutter/images/0zOegrJCYTuapgWgf.webp new file mode 100644 index 0000000000..7ae7603549 Binary files /dev/null and b/sites/www/content/blog/announcing-general-availability-for-the-google-mobile-ads-sdk-for-flutter/images/0zOegrJCYTuapgWgf.webp differ diff --git a/sites/www/content/blog/announcing-general-availability-for-the-google-mobile-ads-sdk-for-flutter/index.md b/sites/www/content/blog/announcing-general-availability-for-the-google-mobile-ads-sdk-for-flutter/index.md new file mode 100644 index 0000000000..c878735f8b --- /dev/null +++ b/sites/www/content/blog/announcing-general-availability-for-the-google-mobile-ads-sdk-for-flutter/index.md @@ -0,0 +1,66 @@ +--- +title: "Announcing General Availability for the Google Mobile Ads SDK for Flutter" +description: "There are many ways for apps to make money: accepting payments as a storefront for a physical business; offering subscriptions or in-app…" +publishDate: 2021-11-16 +author: zoeyfan +image: images/0zOegrJCYTuapgWgf.webp +category: announcements +layout: blog +--- + +<DashImage figure src="images/0zOegrJCYTuapgWgf.webp" /> + + +There are many ways for apps to make money: accepting payments as a storefront for a physical business; offering subscriptions or in-app purchases; or including ads directly in your application. Today, we are excited to announce the [Google Mobile Ads SDK for Flutter](https://pub.dev/packages/google_mobile_ads) is now generally available, after a six-month beta period. Great news for Flutter apps that require the presence of in-app ads! + +## Supported Ads Formats + +The Google Mobile Ads (GMA) SDK for Flutter works on both iOS and Android, and supports loading and displaying all Google [mobile ads formats](https://developers.google.com/admob/flutter/quick-start), including: + +**Banner Ads:** A rectangular ad that occupies a spot within an app’s layout. These ads stay on screen while users interact with the app, either anchored at the top or bottom of the screen, or inline with content as the user scrolls. In addition to the standard fixed-sized banner ads, we also support adaptive banner ads, which shows the most optimal banner size based on the device’s width and height. + +**Interstitial Ads:** A full-screen ad that covers the whole screen of your app. These ads work best when placed at natural breaks or transitions in your app. + +**Rewarded Video Ads:** A rewarded ad unit that allows you to reward users with in-app items for interacting with video ads, playable ads, and surveys. This is one of the most popular ad formats among game developers. + +**Native Ads:** A highly customizable format that allows you to design ads to match the look and the feel of your app content. + +**App Open Ads:** An ad format that monetizes your mobile app loading experience. App open ads are served when a user opens or switches back to your app. + +<DashImage figure src="images/0RZUZCrtJ7df_hnjI.webp" /> + + +## Google AdMob and Ad Manager + +We partnered with the Google Ads team to build this plugin as the official ads solution for Flutter developers. The GMA SDK for Flutter unifies support for both **Google AdMob and Google Ad Manager** into a single plugin. + +If you are not familiar with Google’s ads services, you might not know that [AdMob](https://admob.google.com/home/) and [Ad Manager](http://admanager.google.com/home) are two different products with different monetization capabilities. The AdMob platform is designed for mobile developers who want to earn money from ads and gain actionable insights to grow the app business. The Ad Manager platform is designed for publishers who have significant direct sales or multiple inventory types. + +T[he GMA SDK for Flutter](https://pub.dev/packages/google_mobile_ads) unifies these products to help meet your monetization needs by sharing code across iOS and Android and not having to rewrite your code as your needs grow. + +<DashImage figure src="images/0_i1bA47tKTTOaFAU.webp" /> + + +## Mediation and Bidding + +This release also includes a preview of a new mediation feature to help you optimize ad performance. [**Mediation**](https://developers.google.com/admob/flutter/mediation/get-started) helps you manage multiple ad sources used to serve ads to your apps in one place. In addition to Google demand, this lets you also serve ads from non-Google ad networks. Using mediation, you can send incoming ad requests to multiple ad sources and find the best available sources to fill the requests. In addition to traditional mediation, it also supports bidding, which enables ad sources to compete in a real-time auction to fill your ad requests. This helps ensure that you’re getting the highest revenue for your impressions. + +The GMA SDK for Flutter offers the same ads features that are available for both iOS and Android apps. You can use the same tooling to manage your campaigns, track your ads performance, and so on. + +## Early Adopters + +We’ve been working with a few early adopters who were eager to get started with ads support using Flutter ahead of our production release. One of these developers is Lotum, who recently rewrote their very successful word game “[4 Pics 1 Word](https://play.google.com/store/apps/details?id=de.lotum.whatsinthefoto.us&hl=en_US&gl=US)”, a top-grossing app in 50 countries, with over 50 million installs of the Android version alone. They chose Flutter for the rewrite and used the GMA SDK for Flutter to display interstitials and rewarded videos. + +In the words of Petra Langenbacher, the app’s Flutter developer: + +*“We’ve been fine-tuning and optimizing our app for years, and we wondered whether a big rewrite might impact our revenue or user base. But we’ve been pleasantly surprised to see that wasn’t the case at all: we’ve been able to make these changes without any negative impact!”* + +Thank you to Lotum and all the other early adopters who provided us with valuable feedback. We appreciate your help with this initial release. + +## Other Monetization Features + +Besides ads, Flutter offers other ways to monetize apps. For example, the I[n-App Purchase](https://pub.dev/packages/in_app_purchase) plugin allows you to offer additional content in your app like premium services, digital goods, and subscriptions. Flutter’s [Pay ](https://pub.dev/packages/pay)plugin lets your app integrate with Google Pay on Android devices and Apple Pay on iOS. This makes it quick and easy to support both platforms with a seamless experience for your users to pay for things like groceries, retail merchandise, and food delivery. + +For more about our Ads or other monetization offerings, [please check out our newly refreshed website](https://flutter.dev/monetization), where you can find samples, codelabs, and documentation! + +Building a Flutter app is just the start. With our suite of monetization features, we hope to help you launch a successful business with Flutter! \ No newline at end of file diff --git a/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/18VqmXOFJbmKgq41H5VzzoQ.webp b/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/18VqmXOFJbmKgq41H5VzzoQ.webp new file mode 100644 index 0000000000..3ea2e35f52 Binary files /dev/null and b/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/18VqmXOFJbmKgq41H5VzzoQ.webp differ diff --git a/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/1BM-6gIW7qi51ojLzOQWFBw.webp b/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/1BM-6gIW7qi51ojLzOQWFBw.webp new file mode 100644 index 0000000000..e265a2817b Binary files /dev/null and b/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/1BM-6gIW7qi51ojLzOQWFBw.webp differ diff --git a/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/1Ina6wQU6mBfspHKrfB3VHw.webp b/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/1Ina6wQU6mBfspHKrfB3VHw.webp new file mode 100644 index 0000000000..7cca081fe0 Binary files /dev/null and b/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/1Ina6wQU6mBfspHKrfB3VHw.webp differ diff --git a/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/1OqwtegPVcDWjkuE4Xw8BMQ.webp b/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/1OqwtegPVcDWjkuE4Xw8BMQ.webp new file mode 100644 index 0000000000..4fc3fe3917 Binary files /dev/null and b/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/1OqwtegPVcDWjkuE4Xw8BMQ.webp differ diff --git a/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/1gyhT6-IMAj4pb1-wNGPxaQ.webp b/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/1gyhT6-IMAj4pb1-wNGPxaQ.webp new file mode 100644 index 0000000000..9bdb80357a Binary files /dev/null and b/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/1gyhT6-IMAj4pb1-wNGPxaQ.webp differ diff --git a/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/1r-HMeY-tNkR80DbyIzOFTg.webp b/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/1r-HMeY-tNkR80DbyIzOFTg.webp new file mode 100644 index 0000000000..629eb60dfb Binary files /dev/null and b/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/1r-HMeY-tNkR80DbyIzOFTg.webp differ diff --git a/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/1rHBScksI6g6at1HsqSuPhA.webp b/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/1rHBScksI6g6at1HsqSuPhA.webp new file mode 100644 index 0000000000..67da00cfcc Binary files /dev/null and b/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/images/1rHBScksI6g6at1HsqSuPhA.webp differ diff --git a/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/index.md b/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/index.md new file mode 100644 index 0000000000..44cb84cf74 --- /dev/null +++ b/sites/www/content/blog/announcing-our-new-dart-and-flutter-getting-started-experience/index.md @@ -0,0 +1,90 @@ +--- +title: "Announcing our new Dart and Flutter Getting Started experience" +description: "When I joined the Dash DevRel team, my manager gave me an onboarding doc which started like this: “Your new job in one sentence: Improve…" +publishDate: 2026-02-03 +author: ericwindmill +image: images/1rHBScksI6g6at1HsqSuPhA.webp +category: announcements +layout: blog +--- + +When I joined the Dash DevRel team, my manager gave me an onboarding doc which started like this: “Your new job in one sentence: Improve the lives of Dart and Flutter developers.” + +In time, I’ve come to understand the definition of ‘Dart and Flutter developers’ to include those who don’t yet even call themselves Dart and Flutter developers; they’re just Flutter-curious. + +Today, we’ve landed something that we think improves the lives of those future Flutter developers: the [Dart and Flutter Getting Started experience](http://docs.flutter.dev/learn/pathway). + +<DashImage figure src="images/1rHBScksI6g6at1HsqSuPhA.webp" /> + + +## What we built + +The Getting Started experience is a multi-disciplinary learning pathway for programmers who don’t yet know anything about Dart or Flutter. It spans both the Dart and Flutter websites, and combines written tutorials, video series, quizzes, and documentation to take you from setting up Flutter to building a handful of apps designed to teach the fundamentals. + +### A quick install guide for those who just want to try Flutter out. + +Since the release of hot reload on the web, Flutter learners can have the full Flutter experience without having to download platform-specific development environments until they need them. We wrote [a new quick install guide](http://docs.flutter.dev/learn/learn/pathway/quick-install) to reduce the friction of *trying* Flutter. We don’t want it to feel like a commitment before you even start! + +### Two full tutorials: one for Dart, one for Flutter. + +<DashImage figure src="images/18VqmXOFJbmKgq41H5VzzoQ.webp" /> + + +The [Dart tutorial](http://dart.dev/learn) was designed to lead into the Flutter tutorial, but they aren’t dependent on one another. If you’re already confident using a different modern, object-oriented language, you’re free to jump straight into the Flutter tutorial. + +### Four apps that you’ll build from scratch. + +Of all the design questions we asked ourselves, we spent the most time thinking about what the reader will actually build. Will it be one giant app, several discreet cookbook recipe-like pieces of Flutter code, or something in between? + +<DashImage figure src="images/1OqwtegPVcDWjkuE4Xw8BMQ.webp" /> + + +Ultimately, we came to the conclusion that smaller apps provide a better experience for new learners for a few reasons: + +* With larger apps, there’s more room for things to go wrong, which can cause a new learner to become frustrated. + +* The larger the app, there’s more required code that isn’t actually pertinent to what the tutorial is trying to teach. + +* We didn’t want to ask the user to clone a repository with 25 projects with names like flutter_tutorial_step_1. Then you have to bring git and terminal commands into the picture, you have to ask the reader to copy the commands that work their operating system, and so on. It’s just a bunch of unnecessary cruft that gets in the way of *learning.* + +At the same time, the apps need to be more than single-feature apps, else we’ll fail to connect the dots of a feature to the bigger picture. + +### Eight videos accompanying major topics. + +The videos in the Getting Started experience were written to provide the most crucial *context*, so the reader would understand what they were doing when they got to the hands-on-coding part. + +Including these videos is important to me on a personal level too. Many years ago when I was learning Flutter, I felt in awe of the early DevRel team. They cared deeply about teaching Flutter, and they were so genuine and supportive that I wanted to be part of whatever it was they were doing. Since this learning pathway is often going to be the first thing a new Flutter developer interacts with, I wanted to honor that feeling and make Flutter-curious developers feel excited and welcomed to join the community. + +To that end, we use the videos as a way to teach Flutter, but also to introduce a handful of the real-life humans that work to make the Flutter and Dart better everyday — engineers, tech writers, and product managers are all featured. + +<DashImage figure src="images/1r-HMeY-tNkR80DbyIzOFTg.webp" /> + + +### Small quizzes throughout to confirm what you’ve learned. + +We added simple quizzes at the end of each tutorial page. They’re low-stakes and non-blocking. They exist only to give you a bit of feedback and confidence. If you don’t like ’em, skip ‘em! + +<DashImage figure src="images/1Ina6wQU6mBfspHKrfB3VHw.webp" /> + + +### The existing How Flutter Works YouTube series as a conclusion. + +We cap the learning tutorial off with Craig Labenz’s fantastic video series, released a few months ago, called ‘How Flutter Works’. This series teaches you the inner workings of Flutter with just enough depth that it’s still approachable, yet upgrades your ability to write Flutter apps. This is must-learn knowledge on the way to becoming an advanced Flutter developer. + +<DashImage figure src="images/1gyhT6-IMAj4pb1-wNGPxaQ.webp" /> + + +## Website updates + +This tutorial is shipping alongside several updates to the Dart and Flutter websites. You may know that we’ve been migrating our website infrastructure from JavaScript-based static site generators (SSGs) to [Jaspr](https://jaspr.site/), a Dart SSG. The timing of that migration isn’t random! We knew we wanted to do it, and it seemed like the right thing to do before we started work on the considerable updates to the website we made for the Getting Started tutorials. + +As a reader, the most noticeable update is the separation of the main docs articles and the [Learn](http://docs.flutter.dev/learn) section of the website. The learn section of the site is reached by clicking on the Learn button in the site’s header navigation, as seen in this screenshot: + +<DashImage figure src="images/1BM-6gIW7qi51ojLzOQWFBw.webp" /> + + +The Learn section is where we’ll organize all existing and future tutorial-style documentation and articles, in order to keep the main docs easier to navigate. In the header navigation the main docs can be reached with the “User Guides” button (or by clicking the site title). + +## Try it out + +The learning pathway is live at [docs.flutter.dev/learn/pathway](https://docs.flutter.dev/learn/pathway). If you go through it — or even just part of it — please fill out the feedback survey linked on that page. We’re listening, and we’re already thinking about what comes next. \ No newline at end of file diff --git a/sites/www/content/blog/announcing-the-flutter-casual-games-toolkit/images/0NwJbJ1BD7MvehPAA.webp b/sites/www/content/blog/announcing-the-flutter-casual-games-toolkit/images/0NwJbJ1BD7MvehPAA.webp new file mode 100644 index 0000000000..5a72e321d3 Binary files /dev/null and b/sites/www/content/blog/announcing-the-flutter-casual-games-toolkit/images/0NwJbJ1BD7MvehPAA.webp differ diff --git a/sites/www/content/blog/announcing-the-flutter-casual-games-toolkit/images/0zJekiBgTaE-VTXfa.webp b/sites/www/content/blog/announcing-the-flutter-casual-games-toolkit/images/0zJekiBgTaE-VTXfa.webp new file mode 100644 index 0000000000..bddd96e182 Binary files /dev/null and b/sites/www/content/blog/announcing-the-flutter-casual-games-toolkit/images/0zJekiBgTaE-VTXfa.webp differ diff --git a/sites/www/content/blog/announcing-the-flutter-casual-games-toolkit/images/1rIXhQTkBae35i5YkSN8soQ.webp b/sites/www/content/blog/announcing-the-flutter-casual-games-toolkit/images/1rIXhQTkBae35i5YkSN8soQ.webp new file mode 100644 index 0000000000..4181141ea0 Binary files /dev/null and b/sites/www/content/blog/announcing-the-flutter-casual-games-toolkit/images/1rIXhQTkBae35i5YkSN8soQ.webp differ diff --git a/sites/www/content/blog/announcing-the-flutter-casual-games-toolkit/images/1sTD5PMhST3_73n4vnVcWBQ.webp b/sites/www/content/blog/announcing-the-flutter-casual-games-toolkit/images/1sTD5PMhST3_73n4vnVcWBQ.webp new file mode 100644 index 0000000000..c4397b52db Binary files /dev/null and b/sites/www/content/blog/announcing-the-flutter-casual-games-toolkit/images/1sTD5PMhST3_73n4vnVcWBQ.webp differ diff --git a/sites/www/content/blog/announcing-the-flutter-casual-games-toolkit/index.md b/sites/www/content/blog/announcing-the-flutter-casual-games-toolkit/index.md new file mode 100644 index 0000000000..f99daabb8f --- /dev/null +++ b/sites/www/content/blog/announcing-the-flutter-casual-games-toolkit/index.md @@ -0,0 +1,75 @@ +--- +title: "Announcing the Flutter Casual Games Toolkit" +description: "A starter kit of resources to help you get from concept to launch" +publishDate: 2022-05-12 +author: zoeyfan +image: images/1rIXhQTkBae35i5YkSN8soQ.webp +category: announcements +layout: blog +--- + +For most developers, Flutter is an app framework. But there’s also a growing community around casual game development, taking advantage of the hardware-accelerated graphics support provided by Flutter. + +Over the past year, thousands of Flutter games have been published. For example, Lotum, the game company behind the all-time popular word puzzle game [4 Pics 1 Word](https://flutter.dev/showcase/lotum), rewrote the entire game in Flutter. [Flame](https://flame-engine.org/), a community-driven open-source game engine built on top of Flutter, has been steadily growing both its contributors and users. + +<DashImage figure src="images/1rIXhQTkBae35i5YkSN8soQ.webp" alt="Mobile games developed in Flutter" caption="Mobile games developed in Flutter" /> + + +To understand how we might provide better support, we [interviewed several developers](https://medium.com/flutter/perspectives-from-early-adopters-of-flutter-as-a-game-development-tool-f95fb3406d51) who have successfully built and released Flutter mobile games. We asked what they love about creating games using Flutter, and their answers revealed several themes: + +* Flutter is simple to learn and easy to use for building UIs and casual games + +* Flutter allows them to look into the framework source code (no “black boxes”) and have full control of the canvas + +* Flutter has an open ecosystem where you can find (and leverage) many useful packages, plugins, and libraries + +* Flutter is portable — you write your code once and the game compiles to be multiplatform by default + +Meanwhile, study participants mentioned that the biggest challenge in creating a Flutter game is finding resources and learning materials to get started and integrate with platform gaming services. To address these needs, we’re releasing a new starter toolkit to accelerate your game development. + +The [Flutter Casual Games Toolkit](http://flutter.dev/games) offers a specialized template (developed by [Filip Hracek](https://github.com/filiph)) that you can use to build your own game. This starter project provides pre-built “bells & whistles” such as a main menu, a settings page, sound support, and so on, but leaves the fun part to you: building the game! + +<DashImage figure src="images/0NwJbJ1BD7MvehPAA.webp" alt="Tic-Tac-Toe game running on a mobile device" caption="Tic-Tac-Toe game running on a mobile device" /> + + +### Video Tutorial + +To get started, check out the [video tutorial](https://youtu.be/zGgeBNiRy-8) on how to use the game template. It gives you a step-by-step guide on building a [Tic-Tac-Toe game](https://github.com/filiph/tictactoe) (also available for download from the [iOS](https://apps.apple.com/us/app/tic-tac-toe-puzzle-game/id1611729977)/[Android](https://play.google.com/store/apps/details?id=dev.flutter.tictactoe) stores). To see all the nitty-gritty details that Filip experienced as he developed the game, check out his [raw dev log](https://docs.google.com/document/d/e/2PACX-1vRM-pZYVNOcJhCh5-ZHt8jGwWpNvx4KtpJZECHmaUPn9PIOgdTThK5OBRblCM6PQC4skqDRdW0lJnyM/pub). + +### Pre-integrated services + +In addition to the common UI and functional elements you might need for a game, you get pre-integrated modules and sample code for critical services required for game development. For example, the game template integrates the Apple Game Center and Google Play Games Services so you can easily display features like leaderboards and achievements. + +If you plan to monetize your game, the template uses the [Google Mobile Ads SDK](https://pub.dev/packages/google_mobile_ads), and shows you how to implement sample Ads. The template also uses the [In-App purchase](https://pub.dev/packages/in_app_purchase) package allowing you to offer players additional content in your game like premium experiences, digital goods, and subscriptions. + +Lastly, the game template includes [Firebase Crashlytics](https://pub.dev/packages/firebase_crashlytics), so you can gain more insights into any crashes or errors that might occur in your game. All the source code for the game template can be found in this [Flutter samples repository](https://github.com/flutter/samples/tree/master/game_template#readme) on GitHub. + +### Flutter Game Discord Channel + +One of the strengths of Flutter is its helpful and welcoming community. To chat with other Flutter game developers, ask questions, and share best practices, consider joining the [dedicated game channel](https://discord.gg/WY5NwwjBQz) on r/FlutterDev’s Discord server! Big thanks to Flutter community member @Miyoyo for helping create this community space to support Flutter game developers! (By the way, if you are already a member of the r/FlutterDev community, here is a [direct link](https://discord.com/channels/420324994703163402/964110538986651658).) + +### Google Ads and Cloud Credits + +If your game needs Cloud or Firebase services, or you want to promote your game to more users with Ads, you can get up to $900 in credits offered by the [Google Ads](https://ads.google.com/intl/en_us/home/flutter/#!/) and [Cloud](https://cloud.google.com/free?utm_source=flutter&utm_medium=display&utm_campaign=FY22-Q2-flutter-games_get-started&utm_content=-&utm_term=-) teams (terms and limits apply)! + +### Start with mobile, but go beyond + +We’ve learned from past research that the majority of Flutter games today are casual mobile games, so we prioritized mobile scenarios when designing the Flutter Casual Games Toolkit. + +This doesn’t mean that your creativity should be limited to mobile platforms. In fact, the sample [Tic-Tac-Toe game](https://github.com/filiph/tictactoe) can run on the [web](https://filiph.github.io/tictactoe/) and desktop as well! + +<DashImage figure src="images/1sTD5PMhST3_73n4vnVcWBQ.webp" alt="Tic-Tac-Toe game running on the web and desktop" caption="Tic-Tac-Toe game running on the web and desktop" /> + + +Meanwhile, we recently added support for the community-driven game engine Flame on DartPad, so you can explore[ building games with Flame](https://dartpad.dev/?id=3e52ca7b51ba15f989ad880b8b3314a2) on DartPad without downloading the SDK. What’s more, the web-based [pinball game,](https://pinball.flutter.dev/) written by Very Good Ventures (VGV) and launched at Google I/O, was built in Flutter using the Flame engine! To learn how the pinball game was created, check out [the Pinball article](https://medium.com/flutter/i-o-pinball-powered-by-flutter-and-firebase-d22423f3f5d) from the VGV team or [view the code](https://github.com/flutter/pinball). + +<DashImage figure src="images/0zJekiBgTaE-VTXfa.webp" alt="Web-based pinball game" caption="Web-based pinball game" /> + + +Game development is a new but exciting scenario for Flutter! Going forward, we hope to add more codelabs and other resources to help you develop your games. This is our first attempt at making it easier for you, and we are fully aware that there may be many areas for improvement. We’ve recently started collaborating with community member [@wolfenrain](https://github.com/wolfenrain) to triage game-related issues. Please file an issue (or vote on an existing issue) on GitHub if you need product features that you hope the Flutter SDK can better support. + +### flutter.dev/games + +Please check out the dedicated [game development](http://flutter.dev/games) page, where you can learn more about the above-mentioned resources, and also find links to [documentation](http://docs.flutter.dev/resources/games-toolkit), and references to libraries, packages, and tools recommended by game development experts in the Flutter community. + +Since the launch of Flutter 1.0, you have continued to amaze us with great apps, and now we can’t wait to see what exciting games you will launch with Flutter! \ No newline at end of file diff --git a/sites/www/content/blog/announcing-the-flutter-news-toolkit/images/0_Xb_zBUOvsH6CL9A.webp b/sites/www/content/blog/announcing-the-flutter-news-toolkit/images/0_Xb_zBUOvsH6CL9A.webp new file mode 100644 index 0000000000..a2cdc525c4 Binary files /dev/null and b/sites/www/content/blog/announcing-the-flutter-news-toolkit/images/0_Xb_zBUOvsH6CL9A.webp differ diff --git a/sites/www/content/blog/announcing-the-flutter-news-toolkit/images/0n4yFaH37OzEQ9Ys8.webp b/sites/www/content/blog/announcing-the-flutter-news-toolkit/images/0n4yFaH37OzEQ9Ys8.webp new file mode 100644 index 0000000000..15980a0399 Binary files /dev/null and b/sites/www/content/blog/announcing-the-flutter-news-toolkit/images/0n4yFaH37OzEQ9Ys8.webp differ diff --git a/sites/www/content/blog/announcing-the-flutter-news-toolkit/images/1HOaHUtxwx2S4urCrztI88g.webp b/sites/www/content/blog/announcing-the-flutter-news-toolkit/images/1HOaHUtxwx2S4urCrztI88g.webp new file mode 100644 index 0000000000..430821cfc6 Binary files /dev/null and b/sites/www/content/blog/announcing-the-flutter-news-toolkit/images/1HOaHUtxwx2S4urCrztI88g.webp differ diff --git a/sites/www/content/blog/announcing-the-flutter-news-toolkit/index.md b/sites/www/content/blog/announcing-the-flutter-news-toolkit/index.md new file mode 100644 index 0000000000..f036911cf1 --- /dev/null +++ b/sites/www/content/blog/announcing-the-flutter-news-toolkit/index.md @@ -0,0 +1,99 @@ +--- +title: "Announcing the Flutter News Toolkit" +description: "An accelerated toolkit for building mobile apps for news and content" +publishDate: 2023-01-25 +author: zoeyfan +image: images/0_Xb_zBUOvsH6CL9A.webp +category: announcements +layout: blog +--- + +<DashImage figure src="images/0_Xb_zBUOvsH6CL9A.webp" /> + + +Google’s Flutter team has partnered closely with the [Google News Initiative (GNI)](https://newsinitiative.withgoogle.com/) in creating the [Flutter News Toolkit](https://github.com/flutter/news_toolkit) — a free, prebuilt Flutter app template — to accelerate news application development. Some early adopters have already built iOS and Android applications in as little as 6 weeks, claiming up to 80% time savings compared to traditional double-ended development on iOS and Android. + +Three months ago, we announced the Toolkit in an early access period and received an overwhelming number of applications from organizations across the world, so we’re excited to announce the first version of the Flutter News Toolkit is now generally available! + +### Why did we build the Flutter News Toolkit? + +Nowadays, most people turn to smartphones for news. In addition to mobile websites, mobile apps can be a great way for news publishers to engage with existing readers, reach new audiences, and create new revenue streams. But it takes time and effort to design, implement and deploy mobile apps. + +For news publishers who have limited financial and human resources, building apps from scratch is a major undertaking. Here at Google, we want to help developers be successful and productive, so we’re investing in a toolkit that offers all the common functionalities news publishers need, so they can focus on differentiating their brand and content without writing boilerplate code. + +### What features are included in the Flutter News Toolkit? + +We collaborated with industry experts and news publishers globally to identify essential features and user workflows for a news application template. With these pre-built, out-of-the-box features, news publishers only need to connect their data source and customize the UI to reflect their brand. + +Using best practices from [News Consumer Insights](https://news-consumer-insights.appspot.com/), The Flutter News Toolkit includes features, such as: + +* **User onboarding:** re-engage your audience with push notifications permissions; acquire ad tracking permissions + +* **Account creation/login:** authenticate through your platform and user social networks (such as Google login); create passwordless accounts + +* **Content feeds and content pages**: display news articles, videos, pictures, ads, and newsletter sign-up prompts + +* **Monetization**: easily add subscriptions and different ad formats + +* **Search**: allow user searches for articles by keyword or click into popular searches + +* **Settings:** allow user preferences for notifications, login, and ads tracking + +You can use these pre-integrated features out-of-the-box, or easily modify and swap them with other functionalities you prefer. + +<DashImage figure src="images/1HOaHUtxwx2S4urCrztI88g.webp" alt="*Sample features provided in the Flutter News Toolkit*" caption="*Sample features provided in the Flutter News Toolkit*" /> + + +### Early adopters + +The Flutter News Toolkit has already begun helping news publishers across the globe accelerate mobile strategy and achieve business goals. For example, + +* The Standard (one of Kenya’s largest news publishers) claimed time savings of up to 80% when they built [The Standard News](https://play.google.com/store/apps/details?id=ke.co.group.standard.thestandardkenya.thestandard&hl=en&gl=US). + +* Commerce enablement company AnyMind Group built apps for [Khaosod Online](https://play.google.com/store/apps/details?id=co.th.khaosod.mobile&hl=en&gl=US) (one of Thailand’s largest news sites) in 2 weeks; + +* [Boldsports](https://play.google.com/store/apps/details?id=ng.boldsports.news.template&hl=en&gl=US), a Nigerian-based sports news start-up, created an early version within just a few days; + +* Hespress (one of Morocco’s largest news sites) finished its[ English app](https://play.google.com/store/apps/details?id=com.hespress.english&hl=en&gl=US) development within 6 weeks and also reported that the new app drove 93% higher organic user acquisition organically and 50% growth in ad revenue within two months of launching the app. + +Let’s hear from some of them in their own words. + +<YoutubeEmbed id="YnJwjZ3jCTc" title="FOO041 v8" fullwidth="true"/> + + +### Get Started + +If you have been waiting to get access to the Flutter News Toolkit, the wait is over! You can now directly access the [code hosted on Github](https://github.com/flutter/news_toolkit). + +If you are interested in adopting the News Toolkit but need help customizing it to your needs, we’ve also got you covered! Just email us at [flutter-news-core-group@google.com](mailto:flutter-news-core-group@google.com), and we can recommend trusted Flutter agencies to help you with app creation using the Flutter News Toolkit. + +Although the Flutter News Toolkit was originally designed for news organizations, anyone who wants to create flow-based content can use the sample code. Whether you want to create a company newsletter app (see [Medius](https://play.google.com/store/apps/details?id=au.com.dius.news) built by DiUS as an example) or a social media app, you’ll likely find pre-built features like user login, social sharing, and content feeds to be helpful to your app. + +We plan to continue to improve the Flutter News Toolkit as we learn more from publishers and developers. Don’t forget to check out our [video tutorial](https://www.youtube.com/watch?v=dukRAS-OUMM) for more details on the [Flutter News Toolkit](https://github.com/flutter/news_toolkit)! + +## FAQ + +### What is Flutter? + +Flutter is Google’s open-source UI toolkit for building beautiful apps for mobile, web, and desktop from a single codebase. Flutter significantly reduces the time required to build and publish an app. Today, Flutter is [the most popular cross-platform](https://www.statista.com/statistics/869224/worldwide-software-developer-working-hours/) development framework, with over 3 million developers around the world. There are more than 700,000 Flutter apps, including Alibaba, BMW, Google Pay, PUBG, Shein, and Wechat. + +### Is Flutter easy to learn? + +Yes, for developers who have knowledge of other programming languages such as Java, learning Flutter is quick. Besides, we have a wealth of [learning resources](https://flutter.dev/learn) available for you to get started. + +### Can I use other non-Google services such as Ads, analytics, and subscriptions in the app? + +Of course! The Flutter News Toolkit only aims to provide a sample. You are not required to use any service you don’t want. In fact, it is easy to remove or swap the existing integrations with other services. For example, if you want to use non-Google Ads services, you can find other [packages](https://pub.dev/packages?q=platform%3Aandroid+platform%3Aios+ads) on pub.dev. + +### How much work am I required to do to launch the app? + +While the news template can lower the barrier to entry for building news mobile applications, this solution still requires Flutter development work. The template doesn’t support no-code or wysiwyg functionality or tools (such as dashboards) for non-technical users to modify the app branding and styling. + +We have mapped out the deployment process at a high-level with an assumption of 2 Flutter engineers working on the project. As an example, early adopters like Hespress finished their application development within 6 weeks. But we also have customers like AnyMind launched the app within 2 weeks with 1 engineer. + +<DashImage figure src="images/0n4yFaH37OzEQ9Ys8.webp" alt="*Deployment process at a high-level*" caption="*Deployment process at a high-level*" /> + + +### If I don’t have an in-house development team, how can I use the template? + +We can recommend trusted Flutter agencies who can help you take the template to the finish line. You can email [flutter-news-core-group@google.com](mailto:flutter-news-core-group@google.com) for more information. As a disclaimer, Google is not associated with these agencies, and we do not take any revenue share from them. \ No newline at end of file diff --git a/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0-G6C7XhJ0Dt1YP_W.webp b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0-G6C7XhJ0Dt1YP_W.webp new file mode 100644 index 0000000000..74b81e45aa Binary files /dev/null and b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0-G6C7XhJ0Dt1YP_W.webp differ diff --git a/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0-_mzr5eRCScnnW6P.webp b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0-_mzr5eRCScnnW6P.webp new file mode 100644 index 0000000000..f1acfb0d70 Binary files /dev/null and b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0-_mzr5eRCScnnW6P.webp differ diff --git a/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0Cv4DedVkWjqpQI6k.gif b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0Cv4DedVkWjqpQI6k.gif new file mode 100644 index 0000000000..ce7258cbf3 Binary files /dev/null and b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0Cv4DedVkWjqpQI6k.gif differ diff --git a/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0El3UlTpr8NMOn23v.webp b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0El3UlTpr8NMOn23v.webp new file mode 100644 index 0000000000..c4ee8e55ef Binary files /dev/null and b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0El3UlTpr8NMOn23v.webp differ diff --git a/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0NBpZfqcmNXabcfXw.webp b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0NBpZfqcmNXabcfXw.webp new file mode 100644 index 0000000000..38e0472441 Binary files /dev/null and b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0NBpZfqcmNXabcfXw.webp differ diff --git a/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0dsLnVO7wz1Y3aF6i.webp b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0dsLnVO7wz1Y3aF6i.webp new file mode 100644 index 0000000000..4fc3cff472 Binary files /dev/null and b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0dsLnVO7wz1Y3aF6i.webp differ diff --git a/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0f_uqnrRyCc2QtHwm.webp b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0f_uqnrRyCc2QtHwm.webp new file mode 100644 index 0000000000..d45054c33c Binary files /dev/null and b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0f_uqnrRyCc2QtHwm.webp differ diff --git a/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0qBSPIHNDApk5U0UE.webp b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0qBSPIHNDApk5U0UE.webp new file mode 100644 index 0000000000..5c69d063a9 Binary files /dev/null and b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/0qBSPIHNDApk5U0UE.webp differ diff --git a/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/1RpaR1pTpRa0PUdNdfv4njA.webp b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/1RpaR1pTpRa0PUdNdfv4njA.webp new file mode 100644 index 0000000000..eb3e363647 Binary files /dev/null and b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/images/1RpaR1pTpRa0PUdNdfv4njA.webp differ diff --git a/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/index.md b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/index.md new file mode 100644 index 0000000000..e4669e3c8a --- /dev/null +++ b/sites/www/content/blog/announcing-the-flutter-puzzle-hack-winners/index.md @@ -0,0 +1,74 @@ +--- +title: "Announcing the Flutter Puzzle Hack winners!" +description: "Drumroll please…!!!" +publishDate: 2022-04-21 +author: kkboateng +image: images/1RpaR1pTpRa0PUdNdfv4njA.webp +category: events +layout: blog +--- + +<DashImage figure src="images/1RpaR1pTpRa0PUdNdfv4njA.webp" /> + + +The [Flutter Puzzle Hack](https://flutter.dev/events/puzzle-hack) asked participants to push their creativity to its limits by reimagining a classic slide puzzle. It’s safe to say the submissions blew the judges away! + +Over 5,500 developers representing 120+ countries, and a range of experience levels participated in the Flutter Puzzle Hack. Before sharing the category winners, we want to celebrate everyone who participated in the challenge. Yet again the Flutter community came together to build something special. We gathered for talks covering [animations](https://www.youtube.com/watch?v=9F6dxDDh9yk), [Firebase hosting](https://www.youtube.com/watch?v=kWjJ0aUq5L0), and [slide puzzle basics](https://www.youtube.com/watch?v=2jZn7ccre9g). And then it was great to see Flutter developers building an incredible array of slide puzzles, [sharing highlights along the way](https://twitter.com/roaakdm/status/1493614287515967492), and [voting for your favorites](https://flutterhack.devpost.com/project-gallery). Thank you all! + +It’s amazing to be part of such a passionate and creative community. Who knew there were so many different ways to build a simple slide puzzle game?? 🤷‍♂️ + +**Without further ado… it’s time for the winners!** + +## **Category Winners** + +### **Best Execution** + +[**Planets**](https://fph-planets.web.app/#/) by Jyotirmoy Paul + +<DashImage figure src="images/0NBpZfqcmNXabcfXw.webp" alt="A Flutter Puzzle Hack Winner" caption="A Flutter Puzzle Hack Winner" /> + + +This project impressed the judges on many levels with its excellent use of animation, code quality, and pure creativity. On top of the code itself, Jyotirmoy shared some great learning moments along the way — [check it out](https://flutterhack.devpost.com/review/submissions/NG5qSUlZT0wrOTREM3ExNVZ4ZUx4ZmJacnVrRTVxU1pRKzdYOGFsVjlsME9Ia0FoLzNweHRkVmt5aldVNE1HZFkzY3dUdjN3ZHBna1h5cFBxTENYYVRGeEFOanptV1FodFN4Vm9BVWpMaVU9LS0wMkRkTzhNeTRxM2pPSEw5dlFkbFdnPT0=--f9621a4ed75f9e521ff0c61d2459f3c3f371d6f1) if you’re interested. + +### **Most Creative** + +[**The Werewolves Escape**](https://puzzle.wolfiz.com/) by Ahmad Ayaz Noor and team + +<DashImage figure src="images/0El3UlTpr8NMOn23v.webp" alt="A Flutter Puzzle Hack Winner" caption="A Flutter Puzzle Hack Winner" /> + + +This feature-rich project combined the best of Flutter across web, mobile, and desktop. The storyline took the slide puzzle game to a completely new level of fun. Check out their [submission page](https://devpost.com/software/the-werewolves-escape) for more information on what this team learned as they built this project. + +### **Best Animation / Design** + +[**Retro Slide Puzzle**](https://ashishbeck.github.io/slide_puzzle/) by Ashish Beck + +<DashImage figure src="images/0-G6C7XhJ0Dt1YP_W.webp" alt="A Flutter Puzzle Hack Winner" caption="A Flutter Puzzle Hack Winner" /> + + +This puzzle’s extra-polished visuals caught the judges’ attention right away. The project incorporates smooth [Rive](https://rive.app/) animations, sound plug-ins, auto-complete, and other features that make it incredibly satisfying to play. + +### **Best Multiplatform** + +[**Slide Party**](https://slideparty.vercel.app/#/) by Duong Bui Dai + +<DashImage figure src="images/0Cv4DedVkWjqpQI6k.gif" alt="A Flutter Puzzle Hack Winner" caption="A Flutter Puzzle Hack Winner" /> + + +Ever wanted to compete with your friends to see who can solve a slide puzzle the fastest? This puzzle lets you do just that! Not only that, but the players don’t even need to be on the same device or operating system, thanks to the flexibility of Flutter. + +## **Over 40 Winners!** + +<DashImage figure src="images/0f_uqnrRyCc2QtHwm.webp" /> + + +<DashImage figure src="images/0qBSPIHNDApk5U0UE.webp" /> + + +<DashImage figure src="images/0dsLnVO7wz1Y3aF6i.webp" /> + + +<DashImage figure src="images/0-_mzr5eRCScnnW6P.webp" /> + + +While these were the category winners, it was almost impossible to select from so many creative ideas. There were more than thirty other named winners of the Flutter Puzzle Hack, and many other great entries besides. Head to [flutter.dev/events/puzzle-hack](https://flutter.dev/events/puzzle-hack) to see the winners and try their puzzles yourself! \ No newline at end of file diff --git a/sites/www/content/blog/announcing-the-flutter-puzzle-hack/images/1RpaR1pTpRa0PUdNdfv4njA.webp b/sites/www/content/blog/announcing-the-flutter-puzzle-hack/images/1RpaR1pTpRa0PUdNdfv4njA.webp new file mode 100644 index 0000000000..eb3e363647 Binary files /dev/null and b/sites/www/content/blog/announcing-the-flutter-puzzle-hack/images/1RpaR1pTpRa0PUdNdfv4njA.webp differ diff --git a/sites/www/content/blog/announcing-the-flutter-puzzle-hack/images/1fzoYmZlFqBLxkXuCDM6IEw.webp b/sites/www/content/blog/announcing-the-flutter-puzzle-hack/images/1fzoYmZlFqBLxkXuCDM6IEw.webp new file mode 100644 index 0000000000..acdd111d09 Binary files /dev/null and b/sites/www/content/blog/announcing-the-flutter-puzzle-hack/images/1fzoYmZlFqBLxkXuCDM6IEw.webp differ diff --git a/sites/www/content/blog/announcing-the-flutter-puzzle-hack/index.md b/sites/www/content/blog/announcing-the-flutter-puzzle-hack/index.md new file mode 100644 index 0000000000..46785fbc7f --- /dev/null +++ b/sites/www/content/blog/announcing-the-flutter-puzzle-hack/index.md @@ -0,0 +1,56 @@ +--- +title: "Announcing the Flutter Puzzle Hack!" +description: "Push your creativity to its limits by reimagining this classic puzzle" +publishDate: 2022-01-06 +author: kkboateng +image: images/1RpaR1pTpRa0PUdNdfv4njA.webp +category: events +layout: blog +--- + +<DashImage figure src="images/1RpaR1pTpRa0PUdNdfv4njA.webp" alt="Flutter Puzzle Hack logo" caption="Flutter Puzzle Hack logo" /> + + +Ready to brush off the holiday cobwebs and try your hand at a fun coding project that will test your knowledge of Flutter? We’re excited to announce the Flutter Puzzle Hack, our latest challenge to see what *you* can do with Flutter and a chance to win one of over $50,000 worth of prizes. We’re asking you to develop the most creative yet solvable slide puzzle you can imagine, targeting Flutter on the web. Whether you create a new design, rebuild the puzzle from scratch, or integrate packages in new, interesting ways, we want to see just how far you can push your imagination. + +To get you started, we’ve written sample code that handles all of the puzzle logic. We’ve also curated loads of other documentation and resources to help you build a compelling submission, so whether you’re an experienced Flutter developer or you’re just joining the Flutter community after reading Flutter Apprentice, there’s something here for you! + +<DashImage figure src="images/1fzoYmZlFqBLxkXuCDM6IEw.webp" /> + + +Be sure to share your solutions on Twitter and tag them using the hashtag #flutterpuzzlehack for a chance to be featured on our Twitter account! + +## **Details** + +**Date**: *All projects must be submitted by February 28, 2022, 1:59pm PT (GMT -7). Winners will be announced on March 23, 2022.* + +**How to Submit**: *Registration and entry submission instructions can be found at [DevPost](https://flutterhack.devpost.com/).* + +**Categories:** *Winners will be chosen in the following categories:* + +* *Overall execution* + +* *Best use of animation & design* + +* *Originality* + +* *Best multi-platform solution* + +* *Community Choice* + +*We’ll also award prizes for submissions that feature content that’s helpful to the Flutter community and submissions that include high-quality feedback for the Flutter team.* + +**Prizes** *include a fully loaded Macbook Pro, cash, Flutter swag, and the opportunity to be featured on Flutter’s promotional channels.* + +## **Product Info Sessions** + +On top of all of those resources, we’re partnering with the Flutter Community YouTube channel to host teams like Google Chrome, Firebase, and Rive to talk about everything from best practices for building on the web to effective use of integrations to take your project to the next level. Check out the [contest page](https://flutterhack.devpost.com/) for a detailed schedule. + +<YoutubeEmbed id="ue2qa2L5CF4" title="Flutter Puzzle Hack Reveal" fullwidth="true"/> + + +## **Stay Connected** + +We’re working with our friends at DevPost for this challenge, which means that you’ll be able to vote for your favorite submissions, connect with other participants and find all relevant contest updates on the DevPost platform. + +Head to [flutterhack.devpost.com ](https://flutterhack.devpost.com/)to get started. Good luck, and have fun! \ No newline at end of file diff --git a/sites/www/content/blog/announcing-the-globalgamers-challenge/images/0BI7eVnRDmTOmFkaQ.webp b/sites/www/content/blog/announcing-the-globalgamers-challenge/images/0BI7eVnRDmTOmFkaQ.webp new file mode 100644 index 0000000000..70b018c3a8 Binary files /dev/null and b/sites/www/content/blog/announcing-the-globalgamers-challenge/images/0BI7eVnRDmTOmFkaQ.webp differ diff --git a/sites/www/content/blog/announcing-the-globalgamers-challenge/images/0L51Psa66pNh99Rsd.webp b/sites/www/content/blog/announcing-the-globalgamers-challenge/images/0L51Psa66pNh99Rsd.webp new file mode 100644 index 0000000000..b70bb0a091 Binary files /dev/null and b/sites/www/content/blog/announcing-the-globalgamers-challenge/images/0L51Psa66pNh99Rsd.webp differ diff --git a/sites/www/content/blog/announcing-the-globalgamers-challenge/images/0e6DUZrDIvURyxTW2.webp b/sites/www/content/blog/announcing-the-globalgamers-challenge/images/0e6DUZrDIvURyxTW2.webp new file mode 100644 index 0000000000..e4a3444cf0 Binary files /dev/null and b/sites/www/content/blog/announcing-the-globalgamers-challenge/images/0e6DUZrDIvURyxTW2.webp differ diff --git a/sites/www/content/blog/announcing-the-globalgamers-challenge/images/0fJPnnrD19GKt6MiK.webp b/sites/www/content/blog/announcing-the-globalgamers-challenge/images/0fJPnnrD19GKt6MiK.webp new file mode 100644 index 0000000000..4c6ef8be4e Binary files /dev/null and b/sites/www/content/blog/announcing-the-globalgamers-challenge/images/0fJPnnrD19GKt6MiK.webp differ diff --git a/sites/www/content/blog/announcing-the-globalgamers-challenge/index.md b/sites/www/content/blog/announcing-the-globalgamers-challenge/index.md new file mode 100644 index 0000000000..f448ffdc57 --- /dev/null +++ b/sites/www/content/blog/announcing-the-globalgamers-challenge/index.md @@ -0,0 +1,98 @@ +--- +title: "Announcing the #GlobalGamers Challenge" +description: "Build epic Flutter games to assist in the battle to defend the planet" +publishDate: 2024-01-09 +author: kkboateng +image: images/0e6DUZrDIvURyxTW2.webp +category: events +layout: blog +--- + +<DashImage figure src="images/0e6DUZrDIvURyxTW2.webp" /> + + +We know Flutter devs love a good [challenge](https://flutter.dev/events/puzzle-hack), so just in time for the new year, we’re excited to announce the next Flutter challenge! + +[The Global Gamers Challenge](http://flutter.dev/global-gamers) is an 8 week contest to design, build, and publish sustainable games, sponsored by Flutter and [Global Citizen](https://www.globalcitizen.org/en/). The contest’s winners will receive a trip to NYC in September 2024 to meet some of the Flutter team for a day of workshops and mentorship, and then celebrate their achievements with up to 60,000 other Global Citizens at Global Citizen Festival 2024. + +## What are sustainable games? + +Sustainable games use the power of play to inspire positive environmental action. Imagine Candy Crush, but instead of crushing candies, you’re crushing plastic pollution! Or, picture a game like [Super Dash](https://superdash.flutter.dev/), but instead of collecting acorns, you’re in Dash’s home figuring out how to cool the house by optimizing a path for airflow using windows instead of the AC. Here are some more ideas inspired by Global Citizen campaigns running right now: + +1. [**Encourage a reduction in home energy use**](https://www.reuters.com/markets/commodities/world-cant-afford-us-style-home-energy-consumption-habits-2023-05-19/) +Data shows that American homes average three times more electricity use than typical homes across the rest of the globe. Can you build a game that helps to reduce reliance on inefficient energy sources? + +1. [**Encourage a reduction in use of single-use plastics**](https://www.timeout.com/travel/best-public-transport-in-the-world) +Southeast Asia has some of the highest levels of plastic pollution in the world. Consider building a game that encourages someone to make a swap, like opting for a reusable water bottle over a single-use one. + +1. [**Encourage use of public transportation for short distances and overland options for longer distances**](https://www.ukri.org/what-we-do/browse-our-areas-of-investment-and-support/understanding-plastic-pollution-impact-on-marine-ecosystems-in-southeast-asia/) +Europe is home to many of the world’s greatest public transportation systems. Effective games can encourage people to use local public transit for shorter distances, and overland transport, like trains rather than planes) for longer distances. + +We’re confident that games can encourage players to take small, real life, actions that add up to a large impact for the environment. In fact, [if you’d like to take direct action on these items, check out the campaigns Global Citizen is running now](https://www.globalcitizen.org/en/categories/defend-the-planet/). + +<DashImage figure src="images/0BI7eVnRDmTOmFkaQ.webp" /> + + +## Why join the Global Gamers Challenge? + +## Defend the planet + +This contest was inspired by the [Playing for the Planet Alliance](https://www.playing4theplanet.org/), a United Nations-facilitated alliance of 50 game studios and companies, [including Google](https://blog.google/around-the-globe/google-europe/sustainable-gaming-with-the-playing-for-the-planet-alliance/), with a mission to reduce the industry’s environmental impact and **leverage the power of gaming to bring awareness to and coordinate action for important environmental issues like climate change, biodiversity loss, and pollution. + +Games produced by members of the Playing for the Planet Alliance have a combined reach of over 1 billion people. Through the Alliance’s flagship initiative, the [Green Game Jam](https://www.playing4theplanet.org/green-game-jam-2023), participating games have created real-world impact by raising funds to plant 2.75 million trees and raised roughly $1,500,000 USD to protect wildlife and support environmental causes. + +We’ve partnered with the Playing for the Planet Alliance to source [best practices for environmental games](https://igda-website.s3.us-east-2.amazonaws.com/wp-content/uploads/2022/04/06100719/EnvironmentalGameDesignPlaybook_Alpha_Release_Adj.pdf) and added them to the resources kit provided as part of this challenge. This is your chance to build something that helps protect our planet! + +## Learn something new + +Whether you’re a Flutter developer who’s new to games, a game developer who is new to Flutter, or someone new to both game development and Flutter, you’re bound to learn a few tricks while creating a positive impact on the world. + +## Details + +## Timeline + +All projects must be submitted by March 5, 2024, 2:59pm PT (GMT -8). A Top 20 will be announced in late March 2024, and final winners will be announced in May 2024. + +## Submission guidelines + +Registration and entry submission instructions can be found at [DevPost](http://globalgamers.devpost.com/). + +## Awards + +Winners will be judged based on criteria like: + +* Originality and creativity + +* Sustainable action and story + +* Use of animation + +* Effective Multi-platform deployment + +We won’t just award great coding ability, though. We also have prizes for great ideas, demo videos, educational content for your game, and more! + +**Resources** + +We’ve compiled a bunch of [resources](http://flutter.dev/global-gamers/#resources) to help you build a game, including a [guide](http://flutter.dev/global-gamers/#guide) to navigating this challenge, kind of like a game map. It shows you the resources you need, helps you become a Global Citizen through a new [learning journey](https://glblctzn.co/hQ5oyPFbcGb) in the Global Citizen application, and contains instructions on how to register and submit your game. + +## Teams + +We recommend completing this challenge as part of a team. When you [register for the challenge](http://globalgamers.devpost.com/) on Devpost, you’ll be able to share your skillset, your team status, and your ideas. + +<DashImage figure src="images/0fJPnnrD19GKt6MiK.webp" /> + + +So, whether you have a great idea but need teammates with technical skills to bring it to life, or if you have the technical skills, but want a great idea, make sure to fill out your profile accordingly and then peruse the [participants tab](https://globalgamers.devpost.com/participants) and look for folks whose profile details match what you’re looking for! + +<DashImage figure src="images/0L51Psa66pNh99Rsd.webp" /> + + +We’re particularly excited for the ideas that will come from technologists and activists working together towards a common goal. If you need extra support in finding a teammate, read over this [help article](https://help.devpost.com/hc/en-us/articles/360022031411-Participants-page-forming-a-team). + +Finally, note that teams can be any size, but only 3 people from a team will be able to travel to New York City should the project be selected as a finalist. + +## Get started and stay connected + +Check out the official contest site at [flutter.dev/global-gamers](https://flutter.dev/global-gamers) for everything you need to know. You can also visit [DevPost](https://globalgamers.devpost.com/) to register and submit your game. + +Submissions close on March 5th, so don’t wait! We can’t wait to see what you build! \ No newline at end of file diff --git a/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/02tKU55OoZoT6H8Df.gif b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/02tKU55OoZoT6H8Df.gif new file mode 100644 index 0000000000..7bdef83fce Binary files /dev/null and b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/02tKU55OoZoT6H8Df.gif differ diff --git a/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/04TtFim6qaLkM0-rj.webp b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/04TtFim6qaLkM0-rj.webp new file mode 100644 index 0000000000..a7b1508380 Binary files /dev/null and b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/04TtFim6qaLkM0-rj.webp differ diff --git a/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/05-BTF74D9iY2W-eg.gif b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/05-BTF74D9iY2W-eg.gif new file mode 100644 index 0000000000..26bd89aaed Binary files /dev/null and b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/05-BTF74D9iY2W-eg.gif differ diff --git a/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/09PUEQpgttn8Oib0e.gif b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/09PUEQpgttn8Oib0e.gif new file mode 100644 index 0000000000..e9116c534d Binary files /dev/null and b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/09PUEQpgttn8Oib0e.gif differ diff --git a/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/09lzHmRWHxSyGI8pl.gif b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/09lzHmRWHxSyGI8pl.gif new file mode 100644 index 0000000000..a2d5ab1be0 Binary files /dev/null and b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/09lzHmRWHxSyGI8pl.gif differ diff --git a/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/0P1nBcfMDFwveciQu.gif b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/0P1nBcfMDFwveciQu.gif new file mode 100644 index 0000000000..b65573d063 Binary files /dev/null and b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/0P1nBcfMDFwveciQu.gif differ diff --git a/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/0S_bBN0WiR_Q5F08J.webp b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/0S_bBN0WiR_Q5F08J.webp new file mode 100644 index 0000000000..0f02d858c7 Binary files /dev/null and b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/0S_bBN0WiR_Q5F08J.webp differ diff --git a/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/0W8ns7jNJ3jZ0SXbd.webp b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/0W8ns7jNJ3jZ0SXbd.webp new file mode 100644 index 0000000000..5fd1b9dc5a Binary files /dev/null and b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/0W8ns7jNJ3jZ0SXbd.webp differ diff --git a/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/0jof-AXtEcUz1_WFL.gif b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/0jof-AXtEcUz1_WFL.gif new file mode 100644 index 0000000000..c148026273 Binary files /dev/null and b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/0jof-AXtEcUz1_WFL.gif differ diff --git a/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/0m_Lo2d9Tfr1eqyUw.webp b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/0m_Lo2d9Tfr1eqyUw.webp new file mode 100644 index 0000000000..eda4e7a1ae Binary files /dev/null and b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/0m_Lo2d9Tfr1eqyUw.webp differ diff --git a/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/1tC1QJGzd_UQhSNuapBbFlA.gif b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/1tC1QJGzd_UQhSNuapBbFlA.gif new file mode 100644 index 0000000000..d61e4fb6fb Binary files /dev/null and b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/images/1tC1QJGzd_UQhSNuapBbFlA.gif differ diff --git a/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/index.md b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/index.md new file mode 100644 index 0000000000..40832589ab --- /dev/null +++ b/sites/www/content/blog/announcing-the-winners-of-the-global-gamers-challenge/index.md @@ -0,0 +1,141 @@ +--- +title: "Announcing the winners of the Global Gamers Challenge" +description: "Our 10 top projects, and how to stay involved with sustainability games" +publishDate: 2024-05-14 +author: kkboateng +image: images/0S_bBN0WiR_Q5F08J.webp +category: events +layout: blog +--- + +<DashImage figure src="images/0S_bBN0WiR_Q5F08J.webp" /> + + +We asked the Flutter and Global Citizen communities to come together to build epic Flutter games that focused on helping the planet. Hundreds of innovative game projects were submitted by thousands of passionate developers spanning over 50 countries. + +Each project aimed to empower players with knowledge and actions that can protect our planet. From resource conservation to combating pollution, these games have raised our collective awareness of pressing environmental issues while being genuinely fun to play! We’re deeply inspired by the incredible ideas and passion of the entries. + +<YoutubeEmbed id="_GgqdB9md1w" title="" fullwidth="true"/> + + +## One last drumroll for our Top 10 + +For 103 days, these project teams have ideated, built, sought feedback on, and refined their projects. The result — exciting games that encourage us all to think more deeply about the ways we can live more sustainably. Here they are, in alphabetical order! + +### [Better World](https://devpost.com/software/wip-au-flutter-game) + +A variety of environmental themes, colorful visuals and music, and ideas to partner with organizations to promote positive action. + +<DashImage figure src="images/1tC1QJGzd_UQhSNuapBbFlA.gif" /> + + +### [Craftown](https://devpost.com/software/craftown) + +A unique blend of classic real-time strategy and resource management gameplay, with a focus on sustainability themes. + +<DashImage figure src="images/05-BTF74D9iY2W-eg.gif" /> + + +### [EcoShift Chronicles](https://devpost.com/software/ecoshift-chronicles) + +Encourages positive action with charming visuals and sound design, presenting players with relatable choices and their environmental consequences. + +<DashImage figure src="images/02tKU55OoZoT6H8Df.gif" /> + + +### [Gomiland](https://devpost.com/software/gomiland) + +Visually appealing, educational, waste management game, inspired by Japanese culture. + +<DashImage figure src="images/09PUEQpgttn8Oib0e.gif" /> + + +### [Last Bottle](https://devpost.com/software/last-bottle) + +Charming design and collectible elements that effectively communicate its core message about the limitations of recycling and thinking critically about environmental issues. + +<DashImage figure src="images/0W8ns7jNJ3jZ0SXbd.webp" /> + + +### [MGame — A waste management game](https://devpost.com/software/mgame-a-waste-management-game) + +Ambitious simulation game that offers an engaging take on complex waste management systems, reminiscent of classic city-building titles. + +<DashImage figure src="images/0jof-AXtEcUz1_WFL.gif" /> + + +### [Ocean Rangers](https://devpost.com/software/projectocean) + +A compelling narrative, engaging characters, and strategic gameplay elements offer a unique take on a familiar genre with an environmental twist. + +<DashImage figure src="images/0P1nBcfMDFwveciQu.gif" /> + + +### [PlasticPunk](https://devpost.com/software/plasticpunk) + +City-builder style game presenting a complex environmental challenge through engaging gameplay and appealing visuals. + +<DashImage figure src="images/09lzHmRWHxSyGI8pl.gif" /> + + +### [Waste Wise](https://devpost.com/software/waste-wise) + +Waste sorting game featuring intuitive gameplay and effectively educating players on proper waste disposal. + +<DashImage figure src="images/0m_Lo2d9Tfr1eqyUw.webp" /> + + +### [Whaley’s Bins Waste Sorting](https://devpost.com/software/whaley-s-bins-waste-sorting) + +Waste sorting game with a creative approach to recycling education, featuring distinctive pixel art visuals. + +<DashImage figure src="images/04TtFim6qaLkM0-rj.webp" /> + + +We look forward to hanging out with these teams in New York City for a day of workshops and more in the Google offices and to celebrate with 60,000 other Global Citizens at Global Citizen Festival 2024! + +But these are just 10 of over 30 teams that won prizes from the Global Gamers Challenge, including other members of the Top 20, who you’ll see in the Honorable Mentions list. Head to [flutter.dev/global-gamers/#winners](http://flutter.dev/global-gamers/#winners) to see all of them! + +Congratulations to all of the winners! + +## It doesn’t end here + +*by Lisa Pak, Head of Operations at Playing 4 the Planet Alliance* + +The Global Gamers Challenge might be drawing to a close, but the sustainable games movement doesn’t end here. The games built during this challenge, and similar challenges like the [Global Game Jam](https://globalgamejam.org/), have the potential to reach millions of players, creating a ripple effect of positive environmental change throughout the world. + +To stay connected with this burgeoning movement: + +* **Join the community**: Consider joining online communities and forums dedicated to eco-conscious game design. These spaces offer invaluable opportunities to share your work and exchange ideas with fellow developers + +* **Keep playing sustainability games**: Keep an eye out for [popular games](https://globalgamejam.org/) that include green activations in the form of time limited events, challenges, and new levels, to engage their players on this important topic. Our goal is to reach as many people as possible, and these could be a great source of inspiration. + +* **Make your own voice heard**: Write about your experiences with sustainability game development, mentor aspiring developers, or organize your own initiatives. In sharing your knowledge and expertise, whether technical or environmental, you not only support the growth of the community but also inspire others to create games that make a positive impact on the planet. + +## **Keep building** + +*by Clayton Whittle, Co-Chair, Climate SIG* + +Sustainability in gaming includes promoting the movement itself, prioritizing climate and sustainability concerns in your team’s game design practice, and persisting as ambassadors for a sustainability games industry. + +Green game design is an evolving field, with increasingly refined practices for creating games that yield tangible and measurable impact. While there are no universal rules for crafting a green game, adhering to standard guidelines can significantly benefit any game design team. + +* **Know your audience**: Effective sustainability games begin with understanding your audience’s needs and capabilities. Since sustainability games aim to transform players in some way, understanding your audience is crucial for guiding this transformation. Without knowing where your audience stands initially, it’s challenging to lead them to a desired endpoint. + +* **Be specific**: Focus your message to increase its effectiveness. Trying to change a player’s entire worldview with one game is unrealistic. Instead, identify specific areas where change makes sense. + +* **Promote effective actions**: Equip players with actionable knowledge. Teach them impactful actions they can take to make a difference. Players are more likely to engage if they see how their actions can influence ecosystems or society on a large scale. + +* **Beware of [greenwashing](https://en.wikipedia.org/wiki/Greenwashing)**: Avoid superficial sustainability efforts. Greenwashing involves token sustainability elements that don’t genuinely integrate sustainability issues. While adding green artwork or naming a character “Captain Recycle” might seem appealing, players see through these superficial connections if the gameplay or narrative lacks meaningful sustainability actions. + +Remember, there are countless ways to develop impactful green games. Your ideas and designs might extend beyond this guidance into uncharted territories. What works for your team depends on your mission and audience. However, incorporating these principles into your design discussions help ensure that your team remains focused on its impact mission. + +## **How Flutter can help** + +Here are a couple ways that Flutter can help you spend less time worrying about where your game will run, and more time designing an impactful experience for your mission and audience. + +* **Flutter is productive:** Single codebase development with Flutter means you can write your game just one time, and deploy it across platforms. Features like stateful hot reload help you iterate quickly, speeding up your development flow. + +* **Flutter is easy to learn**: Even if you’re not a professional game developer, Starting with Flutter can be easier than with traditional game development tools. We also offer several resources, like game templates as part of the [Flutter Casual Games Toolkit](http://flutter.dev/games) that give you a launching pad to boost your game development skills. + +Thank you all for joining us on this journey! We can’t wait to see what you build next! \ No newline at end of file diff --git a/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1-nUSSGKplXOzmySlrEUwXg.webp b/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1-nUSSGKplXOzmySlrEUwXg.webp new file mode 100644 index 0000000000..84be0d758f Binary files /dev/null and b/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1-nUSSGKplXOzmySlrEUwXg.webp differ diff --git a/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/14Gdqc7x5Vk85t47tQIocmw.webp b/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/14Gdqc7x5Vk85t47tQIocmw.webp new file mode 100644 index 0000000000..0f767bf477 Binary files /dev/null and b/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/14Gdqc7x5Vk85t47tQIocmw.webp differ diff --git a/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1A6Kateq9HjSt3XADTQzaow.webp b/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1A6Kateq9HjSt3XADTQzaow.webp new file mode 100644 index 0000000000..e497f82d5e Binary files /dev/null and b/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1A6Kateq9HjSt3XADTQzaow.webp differ diff --git a/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1Ep_TKKdQqNDhNGtYjXSmzQ.webp b/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1Ep_TKKdQqNDhNGtYjXSmzQ.webp new file mode 100644 index 0000000000..a2eba1789a Binary files /dev/null and b/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1Ep_TKKdQqNDhNGtYjXSmzQ.webp differ diff --git a/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1F_lxL7fE3-MkjobRiJLqpA.webp b/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1F_lxL7fE3-MkjobRiJLqpA.webp new file mode 100644 index 0000000000..885142fd2b Binary files /dev/null and b/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1F_lxL7fE3-MkjobRiJLqpA.webp differ diff --git a/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1OoVDCYfw18QB91Fpe5jDAg.webp b/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1OoVDCYfw18QB91Fpe5jDAg.webp new file mode 100644 index 0000000000..2efbab57bf Binary files /dev/null and b/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1OoVDCYfw18QB91Fpe5jDAg.webp differ diff --git a/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1aSDHtLA_ZQyiyXX3y9j2Ng.webp b/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1aSDHtLA_ZQyiyXX3y9j2Ng.webp new file mode 100644 index 0000000000..03175a6214 Binary files /dev/null and b/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1aSDHtLA_ZQyiyXX3y9j2Ng.webp differ diff --git a/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1avGeBrHwEcAyptu0aKyc4w.webp b/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1avGeBrHwEcAyptu0aKyc4w.webp new file mode 100644 index 0000000000..3934c4995d Binary files /dev/null and b/sites/www/content/blog/announcing-xd-to-flutter-v2-0/images/1avGeBrHwEcAyptu0aKyc4w.webp differ diff --git a/sites/www/content/blog/announcing-xd-to-flutter-v2-0/index.md b/sites/www/content/blog/announcing-xd-to-flutter-v2-0/index.md new file mode 100644 index 0000000000..8902876bb2 --- /dev/null +++ b/sites/www/content/blog/announcing-xd-to-flutter-v2-0/index.md @@ -0,0 +1,91 @@ +--- +title: "Announcing XD to Flutter v2.0!" +description: "Giving designers a fast path to production Flutter code" +publishDate: 2021-04-29 +author: gskinner +image: images/1OoVDCYfw18QB91Fpe5jDAg.webp +layout: blog +--- + +<DashImage figure src="images/1OoVDCYfw18QB91Fpe5jDAg.webp" /> + + +Flutter is Google’s open-source UI toolkit for building beautiful apps for mobile, desktop, web, and embedded devices from a single codebase. Over the last couple of years, it has become the leading choice for developers who want to build apps for multiple platforms; but designers need a visual tool for prototyping and creating Flutter UI, rather than handcrafting Dart source code. Enter [*XD to Flutter*](https://github.com/AdobeXD/xd-to-flutter-plugin)! + +It’s been almost a year since the first preview release of the plugin, and we’ve continued to refine and improve it with a handful of minor updates, a major v1.0 launch last summer, and now a v2.0 release to match with the launch of Flutter 2! + +## Wait, what is XD to Flutter? + +The name probably gives it away, but the XD to Flutter plugin is a powerful, easy-to-use tool to export your stunning [Adobe XD](https://www.adobe.com/products/xd.html) designs into clean, functional code for Flutter. You can copy code for specific visual elements from your design, export reusable widgets, or even entire views. + +<YoutubeEmbed id="raG7NjM0p0k" title="Adobe XD to Flutter" fullwidth="true"/> + + +That means that, with the XD to Flutter plugin, you can get your designs running on virtually any device with the click of a button. It’s not going to code your whole app for you, but it’ll give you a head start. + +XD to Flutter is built by [gskinner](https://gskinner.com/) in partnership with Adobe, and is published as a plugin for Adobe XD itself, so you can use it with any existing Adobe XD design you’re building. + +## Awesome! So what’s new? + +The initial release of XD to Flutter had great support for outputting all the different visual elements in a design — vector graphics, images, rich text, background blurs, blend modes, shadows, and similar — but the result could be static and inflexible. + +While it was handy for grabbing an icon or text style, we wanted it to do more! XD empowers designers to create dynamic UIs, with tools like responsive layout, scrollable areas, stacks, and grids; we want the plugin to support every one of those capabilities, and with v2.0 we’ve made a lot of progress. + +### Responsive Resize + +XD to Flutter supports the responsive layout features of XD, which lets you “pin” elements within their enclosing parent and precisely control how they resize. + +<DashImage figure src="images/1Ep_TKKdQqNDhNGtYjXSmzQ.webp" alt="Responsive design in Adobe XD" caption="Responsive design in Adobe XD" /> + + +<DashImage figure src="images/1A6Kateq9HjSt3XADTQzaow.webp" alt="Responsive resize in Flutter" caption="Responsive resize in Flutter" /> + + +This is achieved in Flutter by using a custom `Pinned` layout widget in the open-source [adobe_xd](https://pub.dev/packages/adobe_xd) package that developers can leverage directly in their projects. + +<DashImage figure src="images/1F_lxL7fE3-MkjobRiJLqpA.webp" alt="Pinned widget code example" caption="Pinned widget code example" /> + + +### Stacks & Scroll Groups + +“Stacks” and Scroll Groups provide new ways to lay out content on-screen dynamically in Adobe XD. Stacks in XD let you arrange a bunch of different elements in a horizontal or vertical list, with varying spacing between them; they are more similar to a `Flex` widget in Flutter than their namesake `Stack` widget. + +Scroll Groups predictably let you define an area to scroll a larger group of content vertically or horizontally, right inside your design. + +XD to Flutter v2.0 supports both of these features, converting them into common Flutter widgets (`Column`, `Row`, and `SingleChildScrollView`). You can even put a stack into a scroll group to easily create a scrolling list of items. + +<DashImage figure src="images/1avGeBrHwEcAyptu0aKyc4w.webp" alt="Stacks & Scroll Groups in XD (left) and Flutter (right)" caption="Stacks & Scroll Groups in XD (left) and Flutter (right)" /> + + +### Padding & Background Elements + +Another new feature is background elements, which let you designate a visual element as the background for a group. This can be paired with padding to space the background’s edges from the content. + +The Flutter export uses a `Stack` widget to layer the background element behind the content, which is placed into a `Padding` widget. + +<DashImage figure src="images/1-nUSSGKplXOzmySlrEUwXg.webp" alt="Padding & Background in XD (left) and Flutter (right)" caption="Padding & Background in XD (left) and Flutter (right)" /> + + +### Flutter 2 & null safety + +The layout features described above enable much more responsive UI, complimenting Flutter 2’s increased support for form factors like desktop and web. + +Flutter 2 also introduces sound null safety — a language feature that helps developers catch nullability issues before they cause problems in apps. XD to Flutter v2.0 includes a new setting to “Export Null Safe Code”, ensuring that the generated code is future-ready. + +<DashImage figure src="images/14Gdqc7x5Vk85t47tQIocmw.webp" alt="“Export Null Safe Code” setting and output" caption="“Export Null Safe Code” setting and output" /> + + +### Sounds great! How do I get started? + +Whether you’re using it to copy the code for a tricky gradient, or to export fully responsive, parameterized, interactive widgets, it’s simple to join the thousands of creative professionals that are already using the XD to Flutter plugin. + +You can install it by selecting “Browse Plugins…” from Adobe XD’s “Plugin” menu and searching for “Flutter” (strangely, searching for “XD to Flutter” doesn’t work), or just visit [adobe.com/go/xd_to_flutter](https://adobe.com/go/xd_to_flutter). + +<DashImage figure src="images/1aSDHtLA_ZQyiyXX3y9j2Ng.webp" /> + + +Once you have it installed, open the XD to Flutter panel from the plugins panel, and tap the “Need help?” link to check out the [plugin documentation](https://github.com/AdobeXD/xd-to-flutter-plugin/blob/master/README.md). + +Flutter 2 is an exciting step forward for the framework, with a focus on building beautiful apps that run virtually anywhere. At [gskinner](https://gskinner.com/), we’re thrilled to be working with Adobe and Google to ensure that XD to Flutter continues to make the process of faithfully translating a delightful design to a working product even easier. + +Stay tuned for more exciting updates to the plugin soon! \ No newline at end of file diff --git a/sites/www/content/blog/api-design-tip-for-complex-flutter-widgets/images/0-1m66Jxnec3s8Qnb.webp b/sites/www/content/blog/api-design-tip-for-complex-flutter-widgets/images/0-1m66Jxnec3s8Qnb.webp new file mode 100644 index 0000000000..e2a37ac271 Binary files /dev/null and b/sites/www/content/blog/api-design-tip-for-complex-flutter-widgets/images/0-1m66Jxnec3s8Qnb.webp differ diff --git a/sites/www/content/blog/api-design-tip-for-complex-flutter-widgets/index.md b/sites/www/content/blog/api-design-tip-for-complex-flutter-widgets/index.md new file mode 100644 index 0000000000..e1d4b24466 --- /dev/null +++ b/sites/www/content/blog/api-design-tip-for-complex-flutter-widgets/index.md @@ -0,0 +1,58 @@ +--- +title: "API design tip for complex Flutter Widgets" +description: "Asking a widget to be something vs. do something" +publishDate: 2019-03-14 +author: amir_h +category: tutorial +layout: blog +--- + +When writing widgets that manage non-trivial state, it sometimes becomes a struggle to maintain a clean API. I think that, in many cases, this is because one hits the expressiveness boundaries of constructor-based APIs. We’ve been dealing with such cases in the core Flutter team and, while this is not a super complex problem, I find that being unaware of these boundaries can be painful. Naming the problem and taking these limits into account has been useful for me, so I want to share these lessons. + +I’ll start with an example: let’s say we are building a WebView widget that shows embedded web content . For simplicity, let’s start with a minimal API surface. We want to be able to load a specific URL and specify whether JavaScript is enabled. We want the API to feel “Fluttery”, so we create a widget with constructor parameters for these 2 controls. What a simple API! Every Flutter developer will feel at home right away! Want to load the YouTube homepage? Just build the widget: + +`WebView(url: ‘https://youtube.com’ , javascriptEnabled: true)` + +You now want to disable JavaScript? just rebuild the widget: + +`WebView(url: ‘https://youtube.com’ , javascriptEnabled: **false**)` + +Wait, let’s say that before we rebuilt the WebView with `javascriptEnabled=false`, the user clicked a link and the WebView navigated to a different URL. Before the rebuild, the WebView was on https://flutter.dev. What should the widget do when it’s rebuilt with `(url: ‘https://youtube.com’ , javascriptEnabled: **false**)`? Should it return to https://youtube.com? Should it stay in the same page? None of these options are ideal as different apps might want a different thing to happen at different times. The app must be able to express which of the 2 possibilities should happen. + +(Another interesting thought exercise: how would you design a widget-parameters-based API for refreshing the current page?) + +## Be something or do something? + +An application uses a widget’s API for asking it to do things. + +We can, in a hand-wavy way, categorize the things an application asks a widget to do into 2 categories: + +**Be something**, or **do something** (sometimes referred to as *level triggers* and *edge triggers*, respectively). + +When an app tells a widget to have a certain color or, for example, when it tells the WebView widget to have JavaScript enabled, I think of it as a request to “be something”. For example, be a widget with a red background color. For “be something” requests there’s a special contract between the app and the widget: a “be something” request applies to the widget until the next time the app makes a different “be something” request for the same parameter. Back to the color example, the widget cannot change its color property internally, or through any mechanism other than the app’s next “be something” request. So after the app asks the widget to be red, the app knows the widget is red (*) until it asks it to be another color. + +“Be something” requests model nicely as widget parameters, building a widget with a set of parameters is how the app asks it to be something. After building a `Container(color: red)` widget, the app knows that the container is red as long as the app doesn’t rebuild the container with a different parameter. There is no point in making the same “be something” request again, as the widget is already guaranteed to be the widget that complies to the previous request. + +**Do something** requests are requests that don’t require the widget to keep the guarantees described above. Asking a WebView widget to show a specific URL can be nicely modeled as a “do something” request. It is ok if the user navigates to a different page, and the current URL is no longer the last URL the app requested the WebView to show. The app makes no assumption that just because it asked the WebView to show a specific URL, it is still showing it. + +“Do something” requests don’t model well as widget parameters, that is because the widget cannot control when and how it is being rebuilt, it cannot “rebuild itself” with a new URL whenever the user clicks a link. + +So how do we make an API for “do something” requests? So far, I find what works best is making imperative calls. For the URL example, this means having a controller for the WebView and making a `webViewController.loadUrl(..)` call. That’s right, not everything has to be controlled through widget parameters, and it’s no sin to use controllers (in fact, Flutter uses some controllers in the framework, such as [PageController](https://docs.flutter.io/flutter/widgets/PageController-class.html), and the WebViewController in the [webview_flutter](https://pub.dartlang.org/packages/webview_flutter) plugin). + +**There’s a trade-off decision to be made** + +Keep in mind that it is possible to achieve the same functionality of a “do something” API with a “be something” API. For example, for the WebView URL case we can extract the management of the “current location” property out of the widget: when a user clicks a link, the widget invokes an event handler which rebuilds the widget with the new location, basically leaving the “current location” state management to the app. For the “loadUrl” case, managing the “current location” state is pretty complex (imagine a web page with multiple iframes, each with a different scroll position. A user clicks a link in one of these iframes; the new state that the app needs to pass to the WebView is far from trivial and difficult to maintain), so we chose to make it a “do something” API. + +## But I just want to show one specific URL + +Continuing with the WebView URL example, for the simple (and common) case in which the app just wants to show a single page, with no links (maybe a license page), it might be nice to avoid requiring the developer to manage a controller instance. + +In webview_flutter we’ve included an `initialUrl` widget parameter for this purpose. The first time the widget is built initialUrl is loaded. Later changes to this value are ignored when rebuilding the widget. + +While this is nice for simple use cases, it can confuse developers who try to later change the initialUrl value. (See this [GitHub issue](https://github.com/flutter/flutter/issues/21566) for an idea on how confusing this can be.) + +When evaluating whether to add an `initialFoo` parameter, keep in mind that while these are handy for some simple use-cases, they can be confusing for developers. + +That’s my tidbit for today, I found that separating these different kinds of requests to a widget (“be something” vs “do something”) helps when designing APIs for complex widgets (like a WebView or a Map). I hope that this helps you as well! + +** This is not exactly correct, e.g when considering implicit animation widgets like [AnimatedOpacity](https://docs.flutter.io/flutter/widgets/AnimatedOpacity-class.html), the app does not know that the widget is at opacity ‘1.0’ after rebuilding the widget, but it can still be considered as a “be something” request: be the widget that animates opacity to 1.0 (whether that animation was complete already or is still running)* \ No newline at end of file diff --git a/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/04kA8p14vcnIS5cOG.webp b/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/04kA8p14vcnIS5cOG.webp new file mode 100644 index 0000000000..2ef4086f0a Binary files /dev/null and b/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/04kA8p14vcnIS5cOG.webp differ diff --git a/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/08JtwdBejYL_ez5rJ.webp b/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/08JtwdBejYL_ez5rJ.webp new file mode 100644 index 0000000000..75d4887118 Binary files /dev/null and b/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/08JtwdBejYL_ez5rJ.webp differ diff --git a/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/0AkXrQy4YPsBUoYLA.webp b/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/0AkXrQy4YPsBUoYLA.webp new file mode 100644 index 0000000000..2d900ec01f Binary files /dev/null and b/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/0AkXrQy4YPsBUoYLA.webp differ diff --git a/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/0Z0mRRjjnEDQWqAsl.webp b/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/0Z0mRRjjnEDQWqAsl.webp new file mode 100644 index 0000000000..5d23a4976d Binary files /dev/null and b/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/0Z0mRRjjnEDQWqAsl.webp differ diff --git a/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/0hs9oiR_zpBhkzT9u.webp b/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/0hs9oiR_zpBhkzT9u.webp new file mode 100644 index 0000000000..e0f6c3ba14 Binary files /dev/null and b/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/0hs9oiR_zpBhkzT9u.webp differ diff --git a/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/0jjBvOh6G9e9TwLQ1.webp b/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/0jjBvOh6G9e9TwLQ1.webp new file mode 100644 index 0000000000..84e1988cfe Binary files /dev/null and b/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/0jjBvOh6G9e9TwLQ1.webp differ diff --git a/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/0vY8rm_jNlGOmZ8eJ.webp b/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/0vY8rm_jNlGOmZ8eJ.webp new file mode 100644 index 0000000000..a267ab0ccf Binary files /dev/null and b/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/images/0vY8rm_jNlGOmZ8eJ.webp differ diff --git a/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/index.md b/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/index.md new file mode 100644 index 0000000000..2c7cd646d1 --- /dev/null +++ b/sites/www/content/blog/are-you-happy-with-flutter-q4-2020-user-survey-results/index.md @@ -0,0 +1,82 @@ +--- +title: "Are you happy with Flutter? — Q4 2020 user survey results" +description: "The Flutter team at Google has been running a quarterly survey program to collect your feedback since 2018. The survey for the last…" +publishDate: 2021-02-11 +author: jayoung-lee +image: images/0hs9oiR_zpBhkzT9u.webp +category: announcements +layout: blog +--- + +## Are you happy with Flutter? — Q4 2020 user survey results + +The Flutter team at Google has been running a quarterly survey program to collect your feedback [since 2018](https://medium.com/flutter/search?q=survey). The survey for the last quarter launched on November 23, 2020 and remained open for 8 days. The team collected 8,285 responses, and we want to share the results with you! Even though this survey was half the length of our usual surveys, we learned a lot from your answers, as always. We truly appreciate those of you who took the time to provide valuable feedback. + +## User satisfaction + +Overall, 92% of respondents were *somewhat satisfied* or *very satisfied* with Flutter. We’re glad that we continue to maintain a high level of satisfaction. + +<DashImage figure src="images/0vY8rm_jNlGOmZ8eJ.webp" alt="*92% of the users were satisfied with Flutter, and 51% were very satisfied.*" caption="*92% of the users were satisfied with Flutter, and 51% were very satisfied.*" /> + + +As you can see from the graph, we learned the following as well: + +* 91% said that Flutter works well for their projects. + +* 90% are committed and want to use Flutter for their next project. + +* 58% said that Flutter is critical to their company’s success. + +These results show that Flutter continues to flourish and is a vital part of the developer toolkit. + +<DashImage figure src="images/0hs9oiR_zpBhkzT9u.webp" alt="*90% of users would prefer to use Flutter for their next project.*" caption="*90% of users would prefer to use Flutter for their next project.*" /> + + +But, we noticed that the overall satisfaction decreased a bit since last quarter’s survey, from 94% to 92%. To understand why you aren’t completely satisfied with various aspects of Flutter, the team closely reviewed the open-ended comments. Thanks to the feedback, the team is aware of major issues and will invest in high priority items. For instance, we are planning to [improve code completion](https://github.com/dart-lang/sdk/projects/24) because we found that it was the most common source of frustration among IDE-related issues. + +## Pain points + +This time, we took a different approach to understanding Flutter’s pain points. We wanted to learn what blocks you from using Flutter more than you do. When we asked, “if not for the following reasons, I would use Flutter more”, 26% of respondents selected *lacking critical libraries* as the reason. In response, we’re partnering with [Baseflow](https://baseflow.com/), [Invertase](https://invertase.io/), and [Codemagic](https://codemagic.io/) to improve the quality of our first-party packages and plugins. While the team is working hard to improve critical libraries identified from previous research, we are also monitoring specific needs from our issue tracker with the tag [would be a good package](https://github.com/flutter/flutter/issues?q=is%3Aopen+label%3A%22would+be+a+good+package%22+sort%3Areactions-%2B1-desc) so that anyone can find what’s missing from the ecosystem and contribute to it. + +As the number of Flutter-compatible packages grows beyond 15,000 and because there’s no way to scale the Flutter team to take on dozens or even hundreds of packages that might be considered critical, we provide a variety of mechanisms for the community to [grade packages](https://pub.dev/help/scoring). We provide these mechanisms so that searches produce high quality results, including likes, static analysis, and the [Flutter Favorite](https://flutter.dev/docs/development/packages-and-plugins/favorites) designation, which is awarded by a group of Flutter community volunteers. Doing this not only encourages you to contribute more packages to cover the critical bases, but the quality of the packages will improve over time because package authors are rewarded for their work with better scores and recognition. We will continue to invest in this area so that the ecosystem becomes self-sustaining and continues to naturally increase in quality. + +<DashImage figure src="images/04kA8p14vcnIS5cOG.webp" alt="*26% of users need more libraries. This result excludes 19% of users who said I am using Flutter enough.*" caption="*26% of users need more libraries. This result excludes 19% of users who said I am using Flutter enough.*" /> + + +To make it easier for those of you working on projects other than Flutter (19%), we will continue to invest in [add-to-app](https://flutter.dev/docs/development/add-to-app), which helps you integrate Flutter modules into existing Android or iOS apps. You can follow the progress of the related features on [GitHub issue 72009](https://github.com/flutter/flutter/issues/72009). + +*Lacking features in the core framework* was selected by 18% of respondents. After we took a closer look at the open-ended comments, the feedback was centered on the boundary between the functionality included in the Flutter framework and the functionality provided by packages. While it’s understandable that some of you want the framework to include more features beyond user-interface APIs, our focus is on what we do best — provide an excellent UI framework. But, we want to provide you with the functionality you need, so we partner with community contributors to supply the domain-specific and platform-specific capabilities through packages and plugins. As previously mentioned, we invested in tools and programs to recognize high quality packages and help you discover them in the ecosystem. We welcome your feedback and ideas to further strengthen our package ecosystem. + +## Online communities + +In this survey, the team wanted to know where you exchange information or ideas with other Flutter developers. While 21% of respondents said that they aren’t active on any online communities, we found that Stack Overflow was the most popular community for Flutter devs to connect with each other. Stack Overflow was chosen by 38% of respondents as the online community where they are most active. + +<DashImage figure src="images/0jjBvOh6G9e9TwLQ1.webp" alt="*Stack Overflow was the main online community for 38% of respondents. This graph omits the 21% who aren’t active on any online communities.*" caption="*Stack Overflow was the main online community for 38% of respondents. This graph omits the 21% who aren’t active on any online communities.*" /> + + +Depending on where the respondent is most active, the rating on the community’s usefulness differed. For example, we asked, “To what degree do you agree or disagree with the following statements about the online community you selected in the previous question?” For the response, *I was able to get answers when I asked other Flutter users in the community questions*, 75% of respondents who are active on Stack Overflow agreed that they can get answers when they asked questions there. Also, 61% of respondents who are mostly active on instant messaging services said that they also got answers. + +<DashImage figure src="images/08JtwdBejYL_ez5rJ.webp" alt="*Respondents were asked to rate how much they agreed with the statement, “I was able to get answers when I asked …” questions, and the results differed by the community they are active in.*" caption="*Respondents were asked to rate how much they agreed with the statement, “I was able to get answers when I asked …” questions, and the results differed by the community they are active in.*" /> + + +This was our first time assessing user distribution online, and your perception about various online communities. Growing a helpful and thriving community is critical to Flutter’s success. We share the results with you in the hope that both the help givers and the help seekers can better use these online communication channels. The Flutter team will monitor this data to guide our support and educational effort, and make sure that you are able to find help and stay connected. + +## Flutter events + +Every year, a number of in-person and online events for the Flutter community are organized by local meetup groups and Flutter contributors, and about 90% of respondents who attend learn something new about Flutter and find that information to be helpful. While these events are effective learning opportunities, through our research, we now know that the majority of respondents (61%) weren’t aware of these events in 2020. Even though we didn’t host a large event in 2020 like we did in 2019 with [Flutter Interact, where we would have publicized these events](https://www.youtube.com/playlist?list=PLjxrf2q8roU0o0wKRJTjyN0pSUA6TI8lg), this percentage was still higher than what we expected. So, going forward, as we plan our [Flutter Engage event](http://events.flutter.dev) on March 3, 2021, we are partnering with regional Google teams to help spread the word. We’re also working closely with local Flutter groups to ensure that our event content reaches a larger percentage of the Flutter community. Everyone is invited to this free online event. + +<DashImage figure src="images/0AkXrQy4YPsBUoYLA.webp" alt="*61% of users who didn’t attend any events in 2020 (both online and offline) were not aware of the events.*" caption="*61% of users who didn’t attend any events in 2020 (both online and offline) were not aware of the events.*" /> + + +<DashImage figure src="images/0Z0mRRjjnEDQWqAsl.webp" alt="*85% or more users learned something new from various Flutter events.*" caption="*85% or more users learned something new from various Flutter events.*" /> + + +## What’s next? + +The next survey will be announced around late February on [flutter.dev](https://flutter.dev/), [@FlutteDev](https://twitter.com/FlutterDev) (And in the [Flutter IDE plugins](https://flutter.dev/docs/get-started/editor?tab=androidstudio). You can also participate in other research by [signing up](https://docs.google.com/forms/d/e/1FAIpQLSe0i4De809KXVCdljGKrjMj3lxhuzbuFKCtY5PEQPCYtGxFMg/viewform) for upcoming UX studies. + +Please continue contributing your thoughts as the team seeks answers to important questions. + +Again, thank you to everyone who participated in this survey and provided valuable feedback. We appreciate your time and effort! + +See you at [Flutter Engage](https://events.flutter.dev/)! \ No newline at end of file diff --git a/sites/www/content/blog/authors/00yA7OjxKWthRwjK2.webp b/sites/www/content/blog/authors/00yA7OjxKWthRwjK2.webp new file mode 100644 index 0000000000..ab622ad51a Binary files /dev/null and b/sites/www/content/blog/authors/00yA7OjxKWthRwjK2.webp differ diff --git a/sites/www/content/blog/authors/06LK838dGSbGJRtat.webp b/sites/www/content/blog/authors/06LK838dGSbGJRtat.webp new file mode 100644 index 0000000000..a8f9d668ab Binary files /dev/null and b/sites/www/content/blog/authors/06LK838dGSbGJRtat.webp differ diff --git a/sites/www/content/blog/authors/06siHzzkrp68l6H-V.jpg b/sites/www/content/blog/authors/06siHzzkrp68l6H-V.jpg new file mode 100644 index 0000000000..404e58a561 Binary files /dev/null and b/sites/www/content/blog/authors/06siHzzkrp68l6H-V.jpg differ diff --git a/sites/www/content/blog/authors/08pUBYXbTsN324CAB.jpg b/sites/www/content/blog/authors/08pUBYXbTsN324CAB.jpg new file mode 100644 index 0000000000..310df67f98 Binary files /dev/null and b/sites/www/content/blog/authors/08pUBYXbTsN324CAB.jpg differ diff --git a/sites/www/content/blog/authors/09ombi9JukiF7Z1-d.webp b/sites/www/content/blog/authors/09ombi9JukiF7Z1-d.webp new file mode 100644 index 0000000000..0a8cc43a88 Binary files /dev/null and b/sites/www/content/blog/authors/09ombi9JukiF7Z1-d.webp differ diff --git a/sites/www/content/blog/authors/0ALqtMy61v8ANAVWO.webp b/sites/www/content/blog/authors/0ALqtMy61v8ANAVWO.webp new file mode 100644 index 0000000000..b323f01723 Binary files /dev/null and b/sites/www/content/blog/authors/0ALqtMy61v8ANAVWO.webp differ diff --git a/sites/www/content/blog/authors/0EUSBYlBzvqL9j_Xr.webp b/sites/www/content/blog/authors/0EUSBYlBzvqL9j_Xr.webp new file mode 100644 index 0000000000..3a433b078c Binary files /dev/null and b/sites/www/content/blog/authors/0EUSBYlBzvqL9j_Xr.webp differ diff --git a/sites/www/content/blog/authors/0IxpUINuAJhraX2My.jpg b/sites/www/content/blog/authors/0IxpUINuAJhraX2My.jpg new file mode 100644 index 0000000000..d5ca49b118 Binary files /dev/null and b/sites/www/content/blog/authors/0IxpUINuAJhraX2My.jpg differ diff --git a/sites/www/content/blog/authors/0Jzj4ct7CmcvOiR3g.webp b/sites/www/content/blog/authors/0Jzj4ct7CmcvOiR3g.webp new file mode 100644 index 0000000000..cd6fd5e938 Binary files /dev/null and b/sites/www/content/blog/authors/0Jzj4ct7CmcvOiR3g.webp differ diff --git a/sites/www/content/blog/authors/0LHz-9FgHd3doKgCM.jpg b/sites/www/content/blog/authors/0LHz-9FgHd3doKgCM.jpg new file mode 100644 index 0000000000..2acbd5c17a Binary files /dev/null and b/sites/www/content/blog/authors/0LHz-9FgHd3doKgCM.jpg differ diff --git a/sites/www/content/blog/authors/0MWQEEWaslbr-PNFW.webp b/sites/www/content/blog/authors/0MWQEEWaslbr-PNFW.webp new file mode 100644 index 0000000000..f6fac843b8 Binary files /dev/null and b/sites/www/content/blog/authors/0MWQEEWaslbr-PNFW.webp differ diff --git a/sites/www/content/blog/authors/0PSrFnoWOMMa0YZUM.jpg b/sites/www/content/blog/authors/0PSrFnoWOMMa0YZUM.jpg new file mode 100644 index 0000000000..0886573796 Binary files /dev/null and b/sites/www/content/blog/authors/0PSrFnoWOMMa0YZUM.jpg differ diff --git a/sites/www/content/blog/authors/0Qgrum-sTozr6lXCm.jpg b/sites/www/content/blog/authors/0Qgrum-sTozr6lXCm.jpg new file mode 100644 index 0000000000..6660944e02 Binary files /dev/null and b/sites/www/content/blog/authors/0Qgrum-sTozr6lXCm.jpg differ diff --git a/sites/www/content/blog/authors/0Qp4aTK0f2iGxt-UF.webp b/sites/www/content/blog/authors/0Qp4aTK0f2iGxt-UF.webp new file mode 100644 index 0000000000..e54d7d26be Binary files /dev/null and b/sites/www/content/blog/authors/0Qp4aTK0f2iGxt-UF.webp differ diff --git a/sites/www/content/blog/authors/0R7VUzyb9_HQm9afP.jpg b/sites/www/content/blog/authors/0R7VUzyb9_HQm9afP.jpg new file mode 100644 index 0000000000..d066001f4e Binary files /dev/null and b/sites/www/content/blog/authors/0R7VUzyb9_HQm9afP.jpg differ diff --git a/sites/www/content/blog/authors/0Rcs4RmjLKoM8oYfZ.webp b/sites/www/content/blog/authors/0Rcs4RmjLKoM8oYfZ.webp new file mode 100644 index 0000000000..ad3cb2d873 Binary files /dev/null and b/sites/www/content/blog/authors/0Rcs4RmjLKoM8oYfZ.webp differ diff --git a/sites/www/content/blog/authors/0SQ_vs0LMPjY6ejrP.webp b/sites/www/content/blog/authors/0SQ_vs0LMPjY6ejrP.webp new file mode 100644 index 0000000000..f2d0ad420b Binary files /dev/null and b/sites/www/content/blog/authors/0SQ_vs0LMPjY6ejrP.webp differ diff --git a/sites/www/content/blog/authors/0UfXdwppirgDYRNaj.webp b/sites/www/content/blog/authors/0UfXdwppirgDYRNaj.webp new file mode 100644 index 0000000000..34467bd111 Binary files /dev/null and b/sites/www/content/blog/authors/0UfXdwppirgDYRNaj.webp differ diff --git a/sites/www/content/blog/authors/0VdlGNyd_YGGPFkrc.jpg b/sites/www/content/blog/authors/0VdlGNyd_YGGPFkrc.jpg new file mode 100644 index 0000000000..2e2997ccce Binary files /dev/null and b/sites/www/content/blog/authors/0VdlGNyd_YGGPFkrc.jpg differ diff --git a/sites/www/content/blog/authors/0Y_CFLc1qadgr3tPK.jpg b/sites/www/content/blog/authors/0Y_CFLc1qadgr3tPK.jpg new file mode 100644 index 0000000000..78bc15b813 Binary files /dev/null and b/sites/www/content/blog/authors/0Y_CFLc1qadgr3tPK.jpg differ diff --git a/sites/www/content/blog/authors/0c8OAb9nT35XgY56T.jpg b/sites/www/content/blog/authors/0c8OAb9nT35XgY56T.jpg new file mode 100644 index 0000000000..583d1d67a8 Binary files /dev/null and b/sites/www/content/blog/authors/0c8OAb9nT35XgY56T.jpg differ diff --git a/sites/www/content/blog/authors/0cNAKYVA08TeS6TVB.webp b/sites/www/content/blog/authors/0cNAKYVA08TeS6TVB.webp new file mode 100644 index 0000000000..ee95a129ef Binary files /dev/null and b/sites/www/content/blog/authors/0cNAKYVA08TeS6TVB.webp differ diff --git a/sites/www/content/blog/authors/0cgL8XWJBpDSy2mHs.jpg b/sites/www/content/blog/authors/0cgL8XWJBpDSy2mHs.jpg new file mode 100644 index 0000000000..9ae94412e7 Binary files /dev/null and b/sites/www/content/blog/authors/0cgL8XWJBpDSy2mHs.jpg differ diff --git a/sites/www/content/blog/authors/0cxUEIlx7f0R9rFrO.jpg b/sites/www/content/blog/authors/0cxUEIlx7f0R9rFrO.jpg new file mode 100644 index 0000000000..3b887981ca Binary files /dev/null and b/sites/www/content/blog/authors/0cxUEIlx7f0R9rFrO.jpg differ diff --git a/sites/www/content/blog/authors/0gNIf0pPqSsP1oOcM.jpg b/sites/www/content/blog/authors/0gNIf0pPqSsP1oOcM.jpg new file mode 100644 index 0000000000..2b0216ab7e Binary files /dev/null and b/sites/www/content/blog/authors/0gNIf0pPqSsP1oOcM.jpg differ diff --git a/sites/www/content/blog/authors/0hPHb0U1EVsdSI-ZK.jpg b/sites/www/content/blog/authors/0hPHb0U1EVsdSI-ZK.jpg new file mode 100644 index 0000000000..ec7722421e Binary files /dev/null and b/sites/www/content/blog/authors/0hPHb0U1EVsdSI-ZK.jpg differ diff --git a/sites/www/content/blog/authors/0jbYlQ-K9Yh-HVUBp.jpg b/sites/www/content/blog/authors/0jbYlQ-K9Yh-HVUBp.jpg new file mode 100644 index 0000000000..252c97f6d1 Binary files /dev/null and b/sites/www/content/blog/authors/0jbYlQ-K9Yh-HVUBp.jpg differ diff --git a/sites/www/content/blog/authors/0lw25USWxDjT-i0kV.jpg b/sites/www/content/blog/authors/0lw25USWxDjT-i0kV.jpg new file mode 100644 index 0000000000..218f4b30af Binary files /dev/null and b/sites/www/content/blog/authors/0lw25USWxDjT-i0kV.jpg differ diff --git a/sites/www/content/blog/authors/0m23OLfjD-pystWVb.jpg b/sites/www/content/blog/authors/0m23OLfjD-pystWVb.jpg new file mode 100644 index 0000000000..39b8038c43 Binary files /dev/null and b/sites/www/content/blog/authors/0m23OLfjD-pystWVb.jpg differ diff --git a/sites/www/content/blog/authors/0ob8GIJbyVIHe-aQu.jpg b/sites/www/content/blog/authors/0ob8GIJbyVIHe-aQu.jpg new file mode 100644 index 0000000000..ac9314950a Binary files /dev/null and b/sites/www/content/blog/authors/0ob8GIJbyVIHe-aQu.jpg differ diff --git a/sites/www/content/blog/authors/0tNv4NL4miSZG0eeS.jpg b/sites/www/content/blog/authors/0tNv4NL4miSZG0eeS.jpg new file mode 100644 index 0000000000..93c1bc8837 Binary files /dev/null and b/sites/www/content/blog/authors/0tNv4NL4miSZG0eeS.jpg differ diff --git a/sites/www/content/blog/authors/0vHty7_58DD6eW53D.webp b/sites/www/content/blog/authors/0vHty7_58DD6eW53D.webp new file mode 100644 index 0000000000..c898230b4e Binary files /dev/null and b/sites/www/content/blog/authors/0vHty7_58DD6eW53D.webp differ diff --git a/sites/www/content/blog/authors/0xM4OOQfKuCiHd5rI.webp b/sites/www/content/blog/authors/0xM4OOQfKuCiHd5rI.webp new file mode 100644 index 0000000000..8602b77c1c Binary files /dev/null and b/sites/www/content/blog/authors/0xM4OOQfKuCiHd5rI.webp differ diff --git a/sites/www/content/blog/authors/0xV6b-152TcEMSjUr.webp b/sites/www/content/blog/authors/0xV6b-152TcEMSjUr.webp new file mode 100644 index 0000000000..cd776c8622 Binary files /dev/null and b/sites/www/content/blog/authors/0xV6b-152TcEMSjUr.webp differ diff --git a/sites/www/content/blog/authors/0z8A7a6gvtNuF-TxZ.jpg b/sites/www/content/blog/authors/0z8A7a6gvtNuF-TxZ.jpg new file mode 100644 index 0000000000..662d7be2d6 Binary files /dev/null and b/sites/www/content/blog/authors/0z8A7a6gvtNuF-TxZ.jpg differ diff --git a/sites/www/content/blog/authors/109ekgauj46kjGlHoi94FwA.jpeg b/sites/www/content/blog/authors/109ekgauj46kjGlHoi94FwA.jpeg new file mode 100644 index 0000000000..65ac3c89f0 Binary files /dev/null and b/sites/www/content/blog/authors/109ekgauj46kjGlHoi94FwA.jpeg differ diff --git a/sites/www/content/blog/authors/10pSgo-h67AMv_gytSDEiJg.webp b/sites/www/content/blog/authors/10pSgo-h67AMv_gytSDEiJg.webp new file mode 100644 index 0000000000..31ee1c7642 Binary files /dev/null and b/sites/www/content/blog/authors/10pSgo-h67AMv_gytSDEiJg.webp differ diff --git a/sites/www/content/blog/authors/12P-T3BTzLSSPrvomuK_oRQ.webp b/sites/www/content/blog/authors/12P-T3BTzLSSPrvomuK_oRQ.webp new file mode 100644 index 0000000000..36522c3caa Binary files /dev/null and b/sites/www/content/blog/authors/12P-T3BTzLSSPrvomuK_oRQ.webp differ diff --git a/sites/www/content/blog/authors/14HRhyWLOwtZEN7MQ8zLuHA.jpeg b/sites/www/content/blog/authors/14HRhyWLOwtZEN7MQ8zLuHA.jpeg new file mode 100644 index 0000000000..7d3406ef22 Binary files /dev/null and b/sites/www/content/blog/authors/14HRhyWLOwtZEN7MQ8zLuHA.jpeg differ diff --git a/sites/www/content/blog/authors/15Yq8VeVDQqM51HKi50phdg.jpeg b/sites/www/content/blog/authors/15Yq8VeVDQqM51HKi50phdg.jpeg new file mode 100644 index 0000000000..90dece000b Binary files /dev/null and b/sites/www/content/blog/authors/15Yq8VeVDQqM51HKi50phdg.jpeg differ diff --git a/sites/www/content/blog/authors/16L6xLJHqC5vlGBrTFHc6xg.jpeg b/sites/www/content/blog/authors/16L6xLJHqC5vlGBrTFHc6xg.jpeg new file mode 100644 index 0000000000..f6cabe651f Binary files /dev/null and b/sites/www/content/blog/authors/16L6xLJHqC5vlGBrTFHc6xg.jpeg differ diff --git a/sites/www/content/blog/authors/19QBbBTP_oiuZGZf0I49AKA.webp b/sites/www/content/blog/authors/19QBbBTP_oiuZGZf0I49AKA.webp new file mode 100644 index 0000000000..21d7b9c5cb Binary files /dev/null and b/sites/www/content/blog/authors/19QBbBTP_oiuZGZf0I49AKA.webp differ diff --git a/sites/www/content/blog/authors/19Ub8P3iIx6Ji3hUUrSCZlg.webp b/sites/www/content/blog/authors/19Ub8P3iIx6Ji3hUUrSCZlg.webp new file mode 100644 index 0000000000..c87bfaddad Binary files /dev/null and b/sites/www/content/blog/authors/19Ub8P3iIx6Ji3hUUrSCZlg.webp differ diff --git a/sites/www/content/blog/authors/1BgqwW36VvDjTOlmJk5VQ-A.jpeg b/sites/www/content/blog/authors/1BgqwW36VvDjTOlmJk5VQ-A.jpeg new file mode 100644 index 0000000000..46bb51d29f Binary files /dev/null and b/sites/www/content/blog/authors/1BgqwW36VvDjTOlmJk5VQ-A.jpeg differ diff --git a/sites/www/content/blog/authors/1EAeJ6vonuQM5daZCF9CfeQ.jpeg b/sites/www/content/blog/authors/1EAeJ6vonuQM5daZCF9CfeQ.jpeg new file mode 100644 index 0000000000..915eb86d6b Binary files /dev/null and b/sites/www/content/blog/authors/1EAeJ6vonuQM5daZCF9CfeQ.jpeg differ diff --git a/sites/www/content/blog/authors/1HEfsynQuQpVrGR-qdGeSvg.jpeg b/sites/www/content/blog/authors/1HEfsynQuQpVrGR-qdGeSvg.jpeg new file mode 100644 index 0000000000..bc84314827 Binary files /dev/null and b/sites/www/content/blog/authors/1HEfsynQuQpVrGR-qdGeSvg.jpeg differ diff --git a/sites/www/content/blog/authors/1HcMDVJ2lxSQDClP1IUCGzg.jpeg b/sites/www/content/blog/authors/1HcMDVJ2lxSQDClP1IUCGzg.jpeg new file mode 100644 index 0000000000..5e11d235a8 Binary files /dev/null and b/sites/www/content/blog/authors/1HcMDVJ2lxSQDClP1IUCGzg.jpeg differ diff --git a/sites/www/content/blog/authors/1JXjnwMAb89ePR-FoV4-5Hg.jpeg b/sites/www/content/blog/authors/1JXjnwMAb89ePR-FoV4-5Hg.jpeg new file mode 100644 index 0000000000..f69403c595 Binary files /dev/null and b/sites/www/content/blog/authors/1JXjnwMAb89ePR-FoV4-5Hg.jpeg differ diff --git a/sites/www/content/blog/authors/1N3hr8E92Mseta1S7yCX2Mg.webp b/sites/www/content/blog/authors/1N3hr8E92Mseta1S7yCX2Mg.webp new file mode 100644 index 0000000000..7cd3b19644 Binary files /dev/null and b/sites/www/content/blog/authors/1N3hr8E92Mseta1S7yCX2Mg.webp differ diff --git a/sites/www/content/blog/authors/1NC_dwnZV11nnMuK4D0mXYA.jpeg b/sites/www/content/blog/authors/1NC_dwnZV11nnMuK4D0mXYA.jpeg new file mode 100644 index 0000000000..042f62ba6a Binary files /dev/null and b/sites/www/content/blog/authors/1NC_dwnZV11nnMuK4D0mXYA.jpeg differ diff --git a/sites/www/content/blog/authors/1Nmt27LItpgVqSBTY8WIPwg.jpeg b/sites/www/content/blog/authors/1Nmt27LItpgVqSBTY8WIPwg.jpeg new file mode 100644 index 0000000000..7d91c7d641 Binary files /dev/null and b/sites/www/content/blog/authors/1Nmt27LItpgVqSBTY8WIPwg.jpeg differ diff --git a/sites/www/content/blog/authors/1PpHwzypsYF2ce75cfkt0_w.webp b/sites/www/content/blog/authors/1PpHwzypsYF2ce75cfkt0_w.webp new file mode 100644 index 0000000000..78fc46ea62 Binary files /dev/null and b/sites/www/content/blog/authors/1PpHwzypsYF2ce75cfkt0_w.webp differ diff --git a/sites/www/content/blog/authors/1QrcTxiCCIAj38rUnnPTryg.jpeg b/sites/www/content/blog/authors/1QrcTxiCCIAj38rUnnPTryg.jpeg new file mode 100644 index 0000000000..e77cb5663c Binary files /dev/null and b/sites/www/content/blog/authors/1QrcTxiCCIAj38rUnnPTryg.jpeg differ diff --git a/sites/www/content/blog/authors/1T1lv65-OBsGrBSs6mvgvqg.jpeg b/sites/www/content/blog/authors/1T1lv65-OBsGrBSs6mvgvqg.jpeg new file mode 100644 index 0000000000..3010b8d0ce Binary files /dev/null and b/sites/www/content/blog/authors/1T1lv65-OBsGrBSs6mvgvqg.jpeg differ diff --git a/sites/www/content/blog/authors/1TDttyOKULZENb7QG-0Wl3w.jpeg b/sites/www/content/blog/authors/1TDttyOKULZENb7QG-0Wl3w.jpeg new file mode 100644 index 0000000000..5a4d0c519f Binary files /dev/null and b/sites/www/content/blog/authors/1TDttyOKULZENb7QG-0Wl3w.jpeg differ diff --git a/sites/www/content/blog/authors/1WKhF9u7jo7OkE26I5PjGiw.webp b/sites/www/content/blog/authors/1WKhF9u7jo7OkE26I5PjGiw.webp new file mode 100644 index 0000000000..ef914ace3d Binary files /dev/null and b/sites/www/content/blog/authors/1WKhF9u7jo7OkE26I5PjGiw.webp differ diff --git a/sites/www/content/blog/authors/1Wio_YseYYApcaC6FSU52aQ.webp b/sites/www/content/blog/authors/1Wio_YseYYApcaC6FSU52aQ.webp new file mode 100644 index 0000000000..ccebbdc208 Binary files /dev/null and b/sites/www/content/blog/authors/1Wio_YseYYApcaC6FSU52aQ.webp differ diff --git a/sites/www/content/blog/authors/1X-6ifCWQoKYCZ-uLGdPa4w.webp b/sites/www/content/blog/authors/1X-6ifCWQoKYCZ-uLGdPa4w.webp new file mode 100644 index 0000000000..f750503432 Binary files /dev/null and b/sites/www/content/blog/authors/1X-6ifCWQoKYCZ-uLGdPa4w.webp differ diff --git a/sites/www/content/blog/authors/1XUX_mTFnhm4BKyT9q9sOUQ.webp b/sites/www/content/blog/authors/1XUX_mTFnhm4BKyT9q9sOUQ.webp new file mode 100644 index 0000000000..27d8ff0c13 Binary files /dev/null and b/sites/www/content/blog/authors/1XUX_mTFnhm4BKyT9q9sOUQ.webp differ diff --git a/sites/www/content/blog/authors/1XqSzX8F7rKZnrHwIVIr71A.jpeg b/sites/www/content/blog/authors/1XqSzX8F7rKZnrHwIVIr71A.jpeg new file mode 100644 index 0000000000..f2c458588d Binary files /dev/null and b/sites/www/content/blog/authors/1XqSzX8F7rKZnrHwIVIr71A.jpeg differ diff --git a/sites/www/content/blog/authors/1Yh3FMfH210BvJATo22X4Aw.jpeg b/sites/www/content/blog/authors/1Yh3FMfH210BvJATo22X4Aw.jpeg new file mode 100644 index 0000000000..c640576850 Binary files /dev/null and b/sites/www/content/blog/authors/1Yh3FMfH210BvJATo22X4Aw.jpeg differ diff --git a/sites/www/content/blog/authors/1_vDVbsLisergcnpUiXcPRQ.webp b/sites/www/content/blog/authors/1_vDVbsLisergcnpUiXcPRQ.webp new file mode 100644 index 0000000000..0fb8095fb4 Binary files /dev/null and b/sites/www/content/blog/authors/1_vDVbsLisergcnpUiXcPRQ.webp differ diff --git a/sites/www/content/blog/authors/1beM676ZukZSi32yoyuhqxw.jpeg b/sites/www/content/blog/authors/1beM676ZukZSi32yoyuhqxw.jpeg new file mode 100644 index 0000000000..2dd05e6353 Binary files /dev/null and b/sites/www/content/blog/authors/1beM676ZukZSi32yoyuhqxw.jpeg differ diff --git a/sites/www/content/blog/authors/1d1hf9rmd3uJL8yb1sPMIDA.webp b/sites/www/content/blog/authors/1d1hf9rmd3uJL8yb1sPMIDA.webp new file mode 100644 index 0000000000..72a98a9f30 Binary files /dev/null and b/sites/www/content/blog/authors/1d1hf9rmd3uJL8yb1sPMIDA.webp differ diff --git a/sites/www/content/blog/authors/1dZx7Dxmr4VXmjc5U8yDf3w.jpeg b/sites/www/content/blog/authors/1dZx7Dxmr4VXmjc5U8yDf3w.jpeg new file mode 100644 index 0000000000..990f12ff4b Binary files /dev/null and b/sites/www/content/blog/authors/1dZx7Dxmr4VXmjc5U8yDf3w.jpeg differ diff --git a/sites/www/content/blog/authors/1f-mpgT2KL1I-4NugTuv2Rg.jpeg b/sites/www/content/blog/authors/1f-mpgT2KL1I-4NugTuv2Rg.jpeg new file mode 100644 index 0000000000..3d929df91e Binary files /dev/null and b/sites/www/content/blog/authors/1f-mpgT2KL1I-4NugTuv2Rg.jpeg differ diff --git a/sites/www/content/blog/authors/1fcJn7cFYnwInCzoug_PoDA.jpeg b/sites/www/content/blog/authors/1fcJn7cFYnwInCzoug_PoDA.jpeg new file mode 100644 index 0000000000..e64ecaad88 Binary files /dev/null and b/sites/www/content/blog/authors/1fcJn7cFYnwInCzoug_PoDA.jpeg differ diff --git a/sites/www/content/blog/authors/1h_lwkzhaAtW5WTkM750_Xw.jpeg b/sites/www/content/blog/authors/1h_lwkzhaAtW5WTkM750_Xw.jpeg new file mode 100644 index 0000000000..e02d5d6ee1 Binary files /dev/null and b/sites/www/content/blog/authors/1h_lwkzhaAtW5WTkM750_Xw.jpeg differ diff --git a/sites/www/content/blog/authors/1iUVmcyWC2Pj7ERaoSF-Onw.jpeg b/sites/www/content/blog/authors/1iUVmcyWC2Pj7ERaoSF-Onw.jpeg new file mode 100644 index 0000000000..ea5b274b75 Binary files /dev/null and b/sites/www/content/blog/authors/1iUVmcyWC2Pj7ERaoSF-Onw.jpeg differ diff --git a/sites/www/content/blog/authors/1krC2R3xE1FCZTdIbXQDT_A.jpeg b/sites/www/content/blog/authors/1krC2R3xE1FCZTdIbXQDT_A.jpeg new file mode 100644 index 0000000000..7b69ed3644 Binary files /dev/null and b/sites/www/content/blog/authors/1krC2R3xE1FCZTdIbXQDT_A.jpeg differ diff --git a/sites/www/content/blog/authors/1nFmOI6gpDMxUgSzNIZ19Kg.webp b/sites/www/content/blog/authors/1nFmOI6gpDMxUgSzNIZ19Kg.webp new file mode 100644 index 0000000000..fe9c040d49 Binary files /dev/null and b/sites/www/content/blog/authors/1nFmOI6gpDMxUgSzNIZ19Kg.webp differ diff --git a/sites/www/content/blog/authors/1osRrOD-rpUZlVPFwpn2_GQ.webp b/sites/www/content/blog/authors/1osRrOD-rpUZlVPFwpn2_GQ.webp new file mode 100644 index 0000000000..542452a141 Binary files /dev/null and b/sites/www/content/blog/authors/1osRrOD-rpUZlVPFwpn2_GQ.webp differ diff --git a/sites/www/content/blog/authors/1snI23_LOekhWjI-XVYtHmQ.jpeg b/sites/www/content/blog/authors/1snI23_LOekhWjI-XVYtHmQ.jpeg new file mode 100644 index 0000000000..fd46c61e70 Binary files /dev/null and b/sites/www/content/blog/authors/1snI23_LOekhWjI-XVYtHmQ.jpeg differ diff --git a/sites/www/content/blog/authors/1tTlm5zfgYWqtvLbIzUByLQ.webp b/sites/www/content/blog/authors/1tTlm5zfgYWqtvLbIzUByLQ.webp new file mode 100644 index 0000000000..a61f8452ae Binary files /dev/null and b/sites/www/content/blog/authors/1tTlm5zfgYWqtvLbIzUByLQ.webp differ diff --git a/sites/www/content/blog/authors/1uLWpn1jOuAz8_lEu1M9Srw.webp b/sites/www/content/blog/authors/1uLWpn1jOuAz8_lEu1M9Srw.webp new file mode 100644 index 0000000000..7fcf376348 Binary files /dev/null and b/sites/www/content/blog/authors/1uLWpn1jOuAz8_lEu1M9Srw.webp differ diff --git a/sites/www/content/blog/authors/1vRMW7uCsV7VsqqC6dIghNA.jpeg b/sites/www/content/blog/authors/1vRMW7uCsV7VsqqC6dIghNA.jpeg new file mode 100644 index 0000000000..5cf6a27282 Binary files /dev/null and b/sites/www/content/blog/authors/1vRMW7uCsV7VsqqC6dIghNA.jpeg differ diff --git a/sites/www/content/blog/authors/1w7Y-bThACd0V1YJ6N7QePg.webp b/sites/www/content/blog/authors/1w7Y-bThACd0V1YJ6N7QePg.webp new file mode 100644 index 0000000000..e3180f8f91 Binary files /dev/null and b/sites/www/content/blog/authors/1w7Y-bThACd0V1YJ6N7QePg.webp differ diff --git a/sites/www/content/blog/authors/1xu-sgiT2AZrrzBVu1oYnoQ.jpeg b/sites/www/content/blog/authors/1xu-sgiT2AZrrzBVu1oYnoQ.jpeg new file mode 100644 index 0000000000..f50c9e813d Binary files /dev/null and b/sites/www/content/blog/authors/1xu-sgiT2AZrrzBVu1oYnoQ.jpeg differ diff --git a/sites/www/content/blog/authors/1yJ9UamxmYZ_VwVDeM4mDzA.jpeg b/sites/www/content/blog/authors/1yJ9UamxmYZ_VwVDeM4mDzA.jpeg new file mode 100644 index 0000000000..2071eb8a94 Binary files /dev/null and b/sites/www/content/blog/authors/1yJ9UamxmYZ_VwVDeM4mDzA.jpeg differ diff --git a/sites/www/content/blog/authors/2AZjKy9ApAXsgYd9ElZSvlg.jpeg b/sites/www/content/blog/authors/2AZjKy9ApAXsgYd9ElZSvlg.jpeg new file mode 100644 index 0000000000..34c1ff45a2 Binary files /dev/null and b/sites/www/content/blog/authors/2AZjKy9ApAXsgYd9ElZSvlg.jpeg differ diff --git a/sites/www/content/blog/authors/2Cl8MkXie_Fr91CEmLGzQtw.jpeg b/sites/www/content/blog/authors/2Cl8MkXie_Fr91CEmLGzQtw.jpeg new file mode 100644 index 0000000000..77c00b1252 Binary files /dev/null and b/sites/www/content/blog/authors/2Cl8MkXie_Fr91CEmLGzQtw.jpeg differ diff --git a/sites/www/content/blog/authors/2G__H_oe3299HvlPy7YkpKg.webp b/sites/www/content/blog/authors/2G__H_oe3299HvlPy7YkpKg.webp new file mode 100644 index 0000000000..78722eb8e7 Binary files /dev/null and b/sites/www/content/blog/authors/2G__H_oe3299HvlPy7YkpKg.webp differ diff --git a/sites/www/content/blog/authors/2Iw2blcHT-LRD9lr8NTx8HQ.jpeg b/sites/www/content/blog/authors/2Iw2blcHT-LRD9lr8NTx8HQ.jpeg new file mode 100644 index 0000000000..de36d989c4 Binary files /dev/null and b/sites/www/content/blog/authors/2Iw2blcHT-LRD9lr8NTx8HQ.jpeg differ diff --git a/sites/www/content/blog/authors/2d-UpSVU0saEV2On5JBvYaQ.jpeg b/sites/www/content/blog/authors/2d-UpSVU0saEV2On5JBvYaQ.jpeg new file mode 100644 index 0000000000..c898aa98ba Binary files /dev/null and b/sites/www/content/blog/authors/2d-UpSVU0saEV2On5JBvYaQ.jpeg differ diff --git a/sites/www/content/blog/authors/2j6fhR3Uq9ariRWaoB1MGPg.webp b/sites/www/content/blog/authors/2j6fhR3Uq9ariRWaoB1MGPg.webp new file mode 100644 index 0000000000..1c572dfbf0 Binary files /dev/null and b/sites/www/content/blog/authors/2j6fhR3Uq9ariRWaoB1MGPg.webp differ diff --git a/sites/www/content/blog/authors/2stkoPD1JQ5tooR-s76YSmw.jpeg b/sites/www/content/blog/authors/2stkoPD1JQ5tooR-s76YSmw.jpeg new file mode 100644 index 0000000000..9252e1d7e0 Binary files /dev/null and b/sites/www/content/blog/authors/2stkoPD1JQ5tooR-s76YSmw.jpeg differ diff --git a/sites/www/content/blog/authors/2wcnmZEJVlZsf2nNvDOziRA.jpeg b/sites/www/content/blog/authors/2wcnmZEJVlZsf2nNvDOziRA.jpeg new file mode 100644 index 0000000000..3fd3ae9715 Binary files /dev/null and b/sites/www/content/blog/authors/2wcnmZEJVlZsf2nNvDOziRA.jpeg differ diff --git a/sites/www/content/blog/authors/abd99.yaml b/sites/www/content/blog/authors/abd99.yaml new file mode 100644 index 0000000000..34d6a4bdcb --- /dev/null +++ b/sites/www/content/blog/authors/abd99.yaml @@ -0,0 +1,9 @@ +name: "Abdullah Deshmukh" +username: "abd99" +bio: "GSoC ’20 & '21 @dart_lang | Mobile App Engineer" +image: "1uLWpn1jOuAz8_lEu1M9Srw.webp" +twitter: "abdullahzakir99" +github: + handle: "abd99" + username: "Abdullah Deshmukh" + avatar_url: "https://avatars.githubusercontent.com/u/30566863?v=4" diff --git a/sites/www/content/blog/authors/abdallahshaban557.yaml b/sites/www/content/blog/authors/abdallahshaban557.yaml new file mode 100644 index 0000000000..9c3a5284b4 --- /dev/null +++ b/sites/www/content/blog/authors/abdallahshaban557.yaml @@ -0,0 +1,9 @@ +name: "Abdallah W Shaban" +username: "abdallah_w_shaban" +bio: "Product Manager @Google" +image: "1beM676ZukZSi32yoyuhqxw.jpeg" +twitter: "AbdallahSh07" +github: + handle: "abdallahshaban557" + username: "Abdallah Shaban" + avatar_url: "https://avatars.githubusercontent.com/u/17932473?v=4" diff --git a/sites/www/content/blog/authors/aguinis.yaml b/sites/www/content/blog/authors/aguinis.yaml new file mode 100644 index 0000000000..47dab1e5fb --- /dev/null +++ b/sites/www/content/blog/authors/aguinis.yaml @@ -0,0 +1,6 @@ +name: "Martin Aguinis" +username: "aguinis" +bio: "Flutter Team @ Google. Born in Argentina." +image: "2AZjKy9ApAXsgYd9ElZSvlg.jpeg" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/amir_h.yaml b/sites/www/content/blog/authors/amir_h.yaml new file mode 100644 index 0000000000..004e1a98e0 --- /dev/null +++ b/sites/www/content/blog/authors/amir_h.yaml @@ -0,0 +1,6 @@ +name: "Amir Hardon" +username: "amir_h" +bio: "Ex-Googler. Helped build Flutter. I'm building a monorepo! Twitter: @AmirHardon." +image: "1BgqwW36VvDjTOlmJk5VQ-A.jpeg" +twitter: "AmirHardon" +github: null diff --git a/sites/www/content/blog/authors/anderdobo.yaml b/sites/www/content/blog/authors/anderdobo.yaml new file mode 100644 index 0000000000..67e8d54a0b --- /dev/null +++ b/sites/www/content/blog/authors/anderdobo.yaml @@ -0,0 +1,9 @@ +name: "Ander Dobo" +username: "anderdobo" +bio: "Global Product Manager | work on Flutter ( a UI framework for building cross-platform apps from a single codebase) at Google" +image: "19QBbBTP_oiuZGZf0I49AKA.webp" +twitter: "anderdobo" +github: + handle: "anderdobo" + username: "Ander Dobo" + avatar_url: "https://avatars.githubusercontent.com/u/5350105?v=4" diff --git a/sites/www/content/blog/authors/andremobilelabonte.yaml b/sites/www/content/blog/authors/andremobilelabonte.yaml new file mode 100644 index 0000000000..f4b9dc8ce7 --- /dev/null +++ b/sites/www/content/blog/authors/andremobilelabonte.yaml @@ -0,0 +1,6 @@ +name: "André Labonté" +username: "andremobilelabonte" +bio: "Product Manager — Material Design" +image: "0MWQEEWaslbr-PNFW.webp" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/anjannarain.yaml b/sites/www/content/blog/authors/anjannarain.yaml new file mode 100644 index 0000000000..8ac76f0f9f --- /dev/null +++ b/sites/www/content/blog/authors/anjannarain.yaml @@ -0,0 +1,6 @@ +name: "Anjan Narain" +username: "anjannarain" +bio: "Flutter Team @ Google." +image: "0Rcs4RmjLKoM8oYfZ.webp" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/antfitch.yaml b/sites/www/content/blog/authors/antfitch.yaml new file mode 100644 index 0000000000..07880e70a2 --- /dev/null +++ b/sites/www/content/blog/authors/antfitch.yaml @@ -0,0 +1,9 @@ +name: "Amanda Fitch" +username: "afitch" +bio: "Flutter and Dart enthusiast!" +image: "1JXjnwMAb89ePR-FoV4-5Hg.jpeg" +twitter: "" +github: + handle: "antfitch" + username: "Amanda Fitch" + avatar_url: "https://avatars.githubusercontent.com/u/18406675?v=4" diff --git a/sites/www/content/blog/authors/arbullard.yaml b/sites/www/content/blog/authors/arbullard.yaml new file mode 100644 index 0000000000..755d733aab --- /dev/null +++ b/sites/www/content/blog/authors/arbullard.yaml @@ -0,0 +1,6 @@ +name: "Anthony Bullard" +username: "arbullard" +bio: "" +image: "0tNv4NL4miSZG0eeS.jpg" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/ayushbherwani1998.yaml b/sites/www/content/blog/authors/ayushbherwani1998.yaml new file mode 100644 index 0000000000..25c6ec9064 --- /dev/null +++ b/sites/www/content/blog/authors/ayushbherwani1998.yaml @@ -0,0 +1,9 @@ +name: "Ayush Bherwani" +username: "ayushbherwani" +bio: "Organizer Flutter Vadodara | GSoC’20 at Dart | Mobile Developer | Solidity Developer" +image: "2Iw2blcHT-LRD9lr8NTx8HQ.jpeg" +twitter: "ayushbherwani" +github: + handle: "AyushBherwani1998" + username: "Ayush Bherwani" + avatar_url: "https://avatars.githubusercontent.com/u/34301187?v=4" diff --git a/sites/www/content/blog/authors/bdero.yaml b/sites/www/content/blog/authors/bdero.yaml new file mode 100644 index 0000000000..7daba3ee73 --- /dev/null +++ b/sites/www/content/blog/authors/bdero.yaml @@ -0,0 +1,9 @@ +name: "Brandon DeRosier" +username: "algebrandon" +bio: "https://twitter.com/algebrandon https://github.com/bdero" +image: "0Jzj4ct7CmcvOiR3g.webp" +twitter: "" +github: + handle: "bdero" + username: "Brandon DeRosier" + avatar_url: "https://avatars.githubusercontent.com/u/919017?v=4" diff --git a/sites/www/content/blog/authors/bkonyi.yaml b/sites/www/content/blog/authors/bkonyi.yaml new file mode 100644 index 0000000000..f1add67cfe --- /dev/null +++ b/sites/www/content/blog/authors/bkonyi.yaml @@ -0,0 +1,9 @@ +name: "Ben Konyi" +username: "bkonyi" +bio: "Google Senior Software Engineer — Dart + Flutter Hacker, DevTools Expert, Amateur Cyclist, and Proud Canadian 🍁" +image: "1d1hf9rmd3uJL8yb1sPMIDA.webp" +twitter: "" +github: + handle: "bkonyi" + username: "Ben Konyi" + avatar_url: "https://avatars.githubusercontent.com/u/24210656?v=4" diff --git a/sites/www/content/blog/authors/brandonbadger.yaml b/sites/www/content/blog/authors/brandonbadger.yaml new file mode 100644 index 0000000000..68f1490146 --- /dev/null +++ b/sites/www/content/blog/authors/brandonbadger.yaml @@ -0,0 +1,6 @@ +name: "Brandon Badger" +username: "brandonbadger" +bio: "Product Director for Google Frameworks and Languages (Flutter, Dart, Go)" +image: "1nFmOI6gpDMxUgSzNIZ19Kg.webp" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/chinmaygarde.yaml b/sites/www/content/blog/authors/chinmaygarde.yaml new file mode 100644 index 0000000000..2a83edcd94 --- /dev/null +++ b/sites/www/content/blog/authors/chinmaygarde.yaml @@ -0,0 +1,9 @@ +name: "Chinmay Garde" +username: "chinmaygarde" +bio: "" +image: "0xM4OOQfKuCiHd5rI.webp" +twitter: "chinmaygarde" +github: + handle: "chinmaygarde" + username: "Chinmay Garde" + avatar_url: "https://avatars.githubusercontent.com/u/44085?v=4" diff --git a/sites/www/content/blog/authors/clocksmith.yaml b/sites/www/content/blog/authors/clocksmith.yaml new file mode 100644 index 0000000000..5cbc4f021c --- /dev/null +++ b/sites/www/content/blog/authors/clocksmith.yaml @@ -0,0 +1,6 @@ +name: "Anthony Robledo" +username: "clocksmith" +bio: "4 * (1 - 1/3 + 1/5 - 1/7 + 1/9 -1/11 + 1/13 - 1/15 + 1/17 - 1/19 + 1/21 - 1/23 + 1/25 - 1/27 + 1/29 - 1/31 + 1/33 - 1/35 + 1/37 - 1/39 + 1/41 -1/43 + ... )" +image: "1fcJn7cFYnwInCzoug_PoDA.jpeg" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/craiglabenz.yaml b/sites/www/content/blog/authors/craiglabenz.yaml new file mode 100644 index 0000000000..0abb4230cd --- /dev/null +++ b/sites/www/content/blog/authors/craiglabenz.yaml @@ -0,0 +1,9 @@ +name: "Craig Labenz" +username: "craiglabenz" +bio: "" +image: "1PpHwzypsYF2ce75cfkt0_w.webp" +twitter: "craig_labenz" +github: + handle: "craiglabenz" + username: "Craig Labenz" + avatar_url: "https://avatars.githubusercontent.com/u/855034?v=4" diff --git a/sites/www/content/blog/authors/csells.yaml b/sites/www/content/blog/authors/csells.yaml new file mode 100644 index 0000000000..06cf49e377 --- /dev/null +++ b/sites/www/content/blog/authors/csells.yaml @@ -0,0 +1,9 @@ +name: "Chris Sells" +username: "csells" +bio: "Chris Sells is a Google Product Manager on the Flutter development experience. He enjoys long walks on the beach and various technologies." +image: "06LK838dGSbGJRtat.webp" +twitter: "" +github: + handle: "csells" + username: "Chris Sells" + avatar_url: "https://avatars.githubusercontent.com/u/2568253?v=4" diff --git a/sites/www/content/blog/authors/devoncarew.yaml b/sites/www/content/blog/authors/devoncarew.yaml new file mode 100644 index 0000000000..d405aef3f7 --- /dev/null +++ b/sites/www/content/blog/authors/devoncarew.yaml @@ -0,0 +1,9 @@ +name: "Devon Carew" +username: "devoncarew" +bio: "Software Engineer @ Google" +image: "1Wio_YseYYApcaC6FSU52aQ.webp" +twitter: "" +github: + handle: "devoncarew" + username: "Devon Carew" + avatar_url: "https://avatars.githubusercontent.com/u/1269969?v=4" diff --git a/sites/www/content/blog/authors/domesticmouse.yaml b/sites/www/content/blog/authors/domesticmouse.yaml new file mode 100644 index 0000000000..9fec12d927 --- /dev/null +++ b/sites/www/content/blog/authors/domesticmouse.yaml @@ -0,0 +1,9 @@ +name: "Brett Morgan" +username: "domesticmouse" +bio: "I am a developer relations engineer for Google’s Flutter and Dart programming language." +image: "0R7VUzyb9_HQm9afP.jpg" +twitter: "DomesticMouse" +github: + handle: "domesticmouse" + username: "Brett Morgan" + avatar_url: "https://avatars.githubusercontent.com/u/30503?v=4" diff --git a/sites/www/content/blog/authors/efortuna.yaml b/sites/www/content/blog/authors/efortuna.yaml new file mode 100644 index 0000000000..232aac07f7 --- /dev/null +++ b/sites/www/content/blog/authors/efortuna.yaml @@ -0,0 +1,9 @@ +name: "Emily Fortuna" +username: "emily_fortuna" +bio: "" +image: "1f-mpgT2KL1I-4NugTuv2Rg.jpeg" +twitter: "" +github: + handle: "efortuna" + username: "Emily Fortuna" + avatar_url: "https://avatars.githubusercontent.com/u/2112792?v=4" diff --git a/sites/www/content/blog/authors/ericwindmill.yaml b/sites/www/content/blog/authors/ericwindmill.yaml new file mode 100644 index 0000000000..719a2c3c32 --- /dev/null +++ b/sites/www/content/blog/authors/ericwindmill.yaml @@ -0,0 +1,9 @@ +name: "Eric Windmill" +username: "ericwindmill" +bio: "Dart Software Engineer. Author of Flutter in Action. Prince enthusiast. Certified Sommelier. ericwindmill.com" +image: "1_vDVbsLisergcnpUiXcPRQ.webp" +twitter: "ericwindmill" +github: + handle: "ericwindmill" + username: "Eric Windmill" + avatar_url: "https://avatars.githubusercontent.com/u/21140344?v=4" diff --git a/sites/www/content/blog/authors/esouthren.yaml b/sites/www/content/blog/authors/esouthren.yaml new file mode 100644 index 0000000000..1b8e37d699 --- /dev/null +++ b/sites/www/content/blog/authors/esouthren.yaml @@ -0,0 +1,9 @@ +name: "Eilidh Southren" +username: "eilidhsouthren" +bio: "Enterprise Engineer @ FlutterFlow" +image: "1X-6ifCWQoKYCZ-uLGdPa4w.webp" +twitter: "" +github: + handle: "esouthren" + username: "Eilidh Southren" + avatar_url: "https://avatars.githubusercontent.com/u/15033982?v=4" diff --git a/sites/www/content/blog/authors/ezra-sandzer-bell.yaml b/sites/www/content/blog/authors/ezra-sandzer-bell.yaml new file mode 100644 index 0000000000..7512a56a17 --- /dev/null +++ b/sites/www/content/blog/authors/ezra-sandzer-bell.yaml @@ -0,0 +1,6 @@ +name: "Ezra Sandzer-Bell" +username: "ezra-sandzer-bell" +bio: "Web Developer" +image: "1tTlm5zfgYWqtvLbIzUByLQ.webp" +twitter: "EzraSandzer" +github: null diff --git a/sites/www/content/blog/authors/filiph.yaml b/sites/www/content/blog/authors/filiph.yaml new file mode 100644 index 0000000000..7f23fd1907 --- /dev/null +++ b/sites/www/content/blog/authors/filiph.yaml @@ -0,0 +1,9 @@ +name: "Filip Hracek" +username: "filiph" +bio: "I’m a pro­gram­ming buff with formal train­ing in jour­nal­ism. I build games, teach pro­gram­ming, explain things, and create silly soft­ware experiments." +image: "1dZx7Dxmr4VXmjc5U8yDf3w.jpeg" +twitter: "" +github: + handle: "filiph" + username: "Filip Hracek" + avatar_url: "https://avatars.githubusercontent.com/u/919717?v=4" diff --git a/sites/www/content/blog/authors/fitzface.yaml b/sites/www/content/blog/authors/fitzface.yaml new file mode 100644 index 0000000000..6d9edbb7ce --- /dev/null +++ b/sites/www/content/blog/authors/fitzface.yaml @@ -0,0 +1,6 @@ +name: "Andrew Fitz Gibbon" +username: "fitzface" +bio: "Developer Advocate @ Google" +image: "2wcnmZEJVlZsf2nNvDOziRA.jpeg" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/gaaclarke.yaml b/sites/www/content/blog/authors/gaaclarke.yaml new file mode 100644 index 0000000000..d620e08c71 --- /dev/null +++ b/sites/www/content/blog/authors/gaaclarke.yaml @@ -0,0 +1,6 @@ +name: "Aaron Clarke" +username: "gaaclarke" +bio: "Currently a developer at Google, working on Flutter. A lover of all things software." +image: "0ALqtMy61v8ANAVWO.webp" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/gskinner.yaml b/sites/www/content/blog/authors/gskinner.yaml new file mode 100644 index 0000000000..6a0e5d59de --- /dev/null +++ b/sites/www/content/blog/authors/gskinner.yaml @@ -0,0 +1,9 @@ +name: "Grant Skinner" +username: "gskinner" +bio: "I run gskinner (gskinner.com), a nimble team of designers, developers, and strategic thinkers building innovative digital solutions for smart clients." +image: "1QrcTxiCCIAj38rUnnPTryg.jpeg" +twitter: "" +github: + handle: "gskinner" + username: "Grant Skinner" + avatar_url: "https://avatars.githubusercontent.com/u/157128?v=4" diff --git a/sites/www/content/blog/authors/guidez.yaml b/sites/www/content/blog/authors/guidez.yaml new file mode 100644 index 0000000000..e716565c82 --- /dev/null +++ b/sites/www/content/blog/authors/guidez.yaml @@ -0,0 +1,6 @@ +name: "Pierre-Louis Guidez" +username: "guidez" +bio: "Software engineer" +image: "" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/haddadniaj.yaml b/sites/www/content/blog/authors/haddadniaj.yaml new file mode 100644 index 0000000000..f70c10adb4 --- /dev/null +++ b/sites/www/content/blog/authors/haddadniaj.yaml @@ -0,0 +1,6 @@ +name: "Justin Haddadnia" +username: "haddadniaj" +bio: "" +image: "0gNIf0pPqSsP1oOcM.jpg" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/hansmuller.yaml b/sites/www/content/blog/authors/hansmuller.yaml new file mode 100644 index 0000000000..bcd7878b2a --- /dev/null +++ b/sites/www/content/blog/authors/hansmuller.yaml @@ -0,0 +1,6 @@ +name: "Hans Muller" +username: "hansmuller" +bio: "" +image: "09ombi9JukiF7Z1-d.webp" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/harry_terkelsen.yaml b/sites/www/content/blog/authors/harry_terkelsen.yaml new file mode 100644 index 0000000000..537293a22e --- /dev/null +++ b/sites/www/content/blog/authors/harry_terkelsen.yaml @@ -0,0 +1,6 @@ +name: "Harry Terkelsen" +username: "harry_terkelsen" +bio: "" +image: "0ob8GIJbyVIHe-aQu.jpg" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/hellobrianjames.yaml b/sites/www/content/blog/authors/hellobrianjames.yaml new file mode 100644 index 0000000000..cca464a422 --- /dev/null +++ b/sites/www/content/blog/authors/hellobrianjames.yaml @@ -0,0 +1,6 @@ +name: "Brian James" +username: "hellobrianjames" +bio: "UX Engineer at Google" +image: "1EAeJ6vonuQM5daZCF9CfeQ.jpeg" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/itsjustkevin.yaml b/sites/www/content/blog/authors/itsjustkevin.yaml new file mode 100644 index 0000000000..9808caf0bd --- /dev/null +++ b/sites/www/content/blog/authors/itsjustkevin.yaml @@ -0,0 +1,9 @@ +name: "Kevin Chisholm" +username: "kevinchisholm" +bio: "Kevin Chisholm is a Technical Program Manager for Dart and Flutter at Google." +image: "1HEfsynQuQpVrGR-qdGeSvg.jpeg" +twitter: "" +github: + handle: "itsjustkevin" + username: "Kevin Chisholm" + avatar_url: "https://avatars.githubusercontent.com/u/11145366?v=4" diff --git a/sites/www/content/blog/authors/ivan.yaml b/sites/www/content/blog/authors/ivan.yaml new file mode 100644 index 0000000000..fae7cf8592 --- /dev/null +++ b/sites/www/content/blog/authors/ivan.yaml @@ -0,0 +1,6 @@ +name: "Ivan Leider" +username: "ivan" +bio: "" +image: "0SQ_vs0LMPjY6ejrP.webp" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/jayoung-lee.yaml b/sites/www/content/blog/authors/jayoung-lee.yaml new file mode 100644 index 0000000000..6a16c12825 --- /dev/null +++ b/sites/www/content/blog/authors/jayoung-lee.yaml @@ -0,0 +1,9 @@ +name: "JaYoung Lee" +username: "jayoung_lee" +bio: "UX Researcher at Google" +image: "109ekgauj46kjGlHoi94FwA.jpeg" +twitter: "" +github: + handle: "jayoung-lee" + username: "JaYoung Lee" + avatar_url: "https://avatars.githubusercontent.com/u/11873270?v=4" diff --git a/sites/www/content/blog/authors/jensjohansen.yaml b/sites/www/content/blog/authors/jensjohansen.yaml new file mode 100644 index 0000000000..6392be7860 --- /dev/null +++ b/sites/www/content/blog/authors/jensjohansen.yaml @@ -0,0 +1,9 @@ +name: "Jens Johansen" +username: "jensj" +bio: "" +image: "0Qp4aTK0f2iGxt-UF.webp" +twitter: "" +github: + handle: "jensjohansen" + username: "John K Johansen" + avatar_url: "https://avatars.githubusercontent.com/u/13370762?v=4" diff --git a/sites/www/content/blog/authors/jjmutter.yaml b/sites/www/content/blog/authors/jjmutter.yaml new file mode 100644 index 0000000000..7da429baf8 --- /dev/null +++ b/sites/www/content/blog/authors/jjmutter.yaml @@ -0,0 +1,6 @@ +name: "John Mutter" +username: "jjmutter" +bio: "John Mutter is the Global Chief Technology Officer of the IBM Experience, Design, and Mobile practice, and a Distinguished Engineer." +image: "1yJ9UamxmYZ_VwVDeM4mDzA.jpeg" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/johnpryan.yaml b/sites/www/content/blog/authors/johnpryan.yaml new file mode 100644 index 0000000000..b1f2448dd6 --- /dev/null +++ b/sites/www/content/blog/authors/johnpryan.yaml @@ -0,0 +1,9 @@ +name: "John Ryan" +username: "ryjohn" +bio: "" +image: "1XqSzX8F7rKZnrHwIVIr71A.jpeg" +twitter: "" +github: + handle: "johnpryan" + username: "John Ryan" + avatar_url: "https://avatars.githubusercontent.com/u/1145719?v=4" diff --git a/sites/www/content/blog/authors/josealba.yaml b/sites/www/content/blog/authors/josealba.yaml new file mode 100644 index 0000000000..bc7481fbf4 --- /dev/null +++ b/sites/www/content/blog/authors/josealba.yaml @@ -0,0 +1,9 @@ +name: "Jose Alba" +username: "josealba199675" +bio: "Recently graduated from university and now works on maintaining the Flutter Material Library." +image: "0hPHb0U1EVsdSI-ZK.jpg" +twitter: "" +github: + handle: "JoseAlba" + username: "Jose Alba" + avatar_url: "https://avatars.githubusercontent.com/u/16763114?v=4" diff --git a/sites/www/content/blog/authors/justinmc.yaml b/sites/www/content/blog/authors/justinmc.yaml new file mode 100644 index 0000000000..19a13746bd --- /dev/null +++ b/sites/www/content/blog/authors/justinmc.yaml @@ -0,0 +1,9 @@ +name: "Justin McCandless" +username: "justinjmcc" +bio: "Software engineer in San Francisco contributing to Flutter." +image: "16L6xLJHqC5vlGBrTFHc6xg.jpeg" +twitter: "justinjmcc" +github: + handle: "justinmc" + username: "Justin McCandless" + avatar_url: "https://avatars.githubusercontent.com/u/389558?v=4" diff --git a/sites/www/content/blog/authors/jwren.yaml b/sites/www/content/blog/authors/jwren.yaml new file mode 100644 index 0000000000..bb5dbfa4a8 --- /dev/null +++ b/sites/www/content/blog/authors/jwren.yaml @@ -0,0 +1,9 @@ +name: "Jaime Wren" +username: "jwren" +bio: "I drive technical strategy for Dart/ Flutter IDEs, defining the roadmap and engineering the key tools that power development across mobile, web, and desktop." +image: "0VdlGNyd_YGGPFkrc.jpg" +twitter: "" +github: + handle: "jwren" + username: "Jaime Wren" + avatar_url: "https://avatars.githubusercontent.com/u/1533520?v=4" diff --git a/sites/www/content/blog/authors/kenzieschmoll.yaml b/sites/www/content/blog/authors/kenzieschmoll.yaml new file mode 100644 index 0000000000..3987cc4535 --- /dev/null +++ b/sites/www/content/blog/authors/kenzieschmoll.yaml @@ -0,0 +1,9 @@ +name: "Kenzie Davisson" +username: "kenzieschmoll" +bio: "Lead for the Flutter DevTools team at Google" +image: "06siHzzkrp68l6H-V.jpg" +twitter: "" +github: + handle: "kenzieschmoll" + username: "Kenzie Davisson" + avatar_url: "https://avatars.githubusercontent.com/u/43759233?v=4" diff --git a/sites/www/content/blog/authors/kevmoo.yaml b/sites/www/content/blog/authors/kevmoo.yaml new file mode 100644 index 0000000000..ca89af4235 --- /dev/null +++ b/sites/www/content/blog/authors/kevmoo.yaml @@ -0,0 +1,9 @@ +name: "Kevin Moore" +username: "kevmoo" +bio: "Google Product Manager working on Dart and Flutter web technologies. Find all of my social links at https://j832.com" +image: "1iUVmcyWC2Pj7ERaoSF-Onw.jpeg" +twitter: "kevmoo" +github: + handle: "kevmoo" + username: "Kevin Moore" + avatar_url: "https://avatars.githubusercontent.com/u/17034?v=4" diff --git a/sites/www/content/blog/authors/kf6gpe.yaml b/sites/www/content/blog/authors/kf6gpe.yaml new file mode 100644 index 0000000000..15dfb08b7b --- /dev/null +++ b/sites/www/content/blog/authors/kf6gpe.yaml @@ -0,0 +1,9 @@ +name: "Ray Rischpater, KF6GPE" +username: "kf6gpe" +bio: "I'm a software engineer, writer, husband, father, Quaker, and ham radio operator. Presently employed by Google as a TPM for Flutter." +image: "2stkoPD1JQ5tooR-s76YSmw.jpeg" +twitter: "kf6gpe" +github: + handle: "kf6gpe" + username: "Ray Rischpater, KF6GPE" + avatar_url: "https://avatars.githubusercontent.com/u/1435716?v=4" diff --git a/sites/www/content/blog/authors/khanhnwin.yaml b/sites/www/content/blog/authors/khanhnwin.yaml new file mode 100644 index 0000000000..a312eb274d --- /dev/null +++ b/sites/www/content/blog/authors/khanhnwin.yaml @@ -0,0 +1,9 @@ +name: "Khanh Nguyen" +username: "yenkhanh" +bio: "" +image: "1Nmt27LItpgVqSBTY8WIPwg.jpeg" +twitter: "" +github: + handle: "khanhnwin" + username: "Khanh Nguyen" + avatar_url: "https://avatars.githubusercontent.com/u/12416400?v=4" diff --git a/sites/www/content/blog/authors/kkboateng.yaml b/sites/www/content/blog/authors/kkboateng.yaml new file mode 100644 index 0000000000..2491a416b2 --- /dev/null +++ b/sites/www/content/blog/authors/kkboateng.yaml @@ -0,0 +1,9 @@ +name: "Kelvin Boateng" +username: "kkboateng" +bio: "" +image: "0IxpUINuAJhraX2My.jpg" +twitter: "" +github: + handle: "kkboateng" + username: "Kelvin Boateng" + avatar_url: "https://avatars.githubusercontent.com/u/106280151?v=4" diff --git a/sites/www/content/blog/authors/ktjlee.yaml b/sites/www/content/blog/authors/ktjlee.yaml new file mode 100644 index 0000000000..48a969cce3 --- /dev/null +++ b/sites/www/content/blog/authors/ktjlee.yaml @@ -0,0 +1,6 @@ +name: "Katie Lee" +username: "ktjlee" +bio: "DA intern on the Flutter team" +image: "1vRMW7uCsV7VsqqC6dIghNA.jpeg" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/lamek.yaml b/sites/www/content/blog/authors/lamek.yaml new file mode 100644 index 0000000000..1a6c0b7c9c --- /dev/null +++ b/sites/www/content/blog/authors/lamek.yaml @@ -0,0 +1,9 @@ +name: "Kevin Lamenzo" +username: "KevinLamenzo" +bio: "" +image: "0z8A7a6gvtNuF-TxZ.jpg" +twitter: "KevinLamenzo" +github: + handle: "lamek" + username: "Kevin Lamenzo" + avatar_url: "https://avatars.githubusercontent.com/u/3738757?v=4" diff --git a/sites/www/content/blog/authors/leighajarett.yaml b/sites/www/content/blog/authors/leighajarett.yaml new file mode 100644 index 0000000000..517fc8d4be --- /dev/null +++ b/sites/www/content/blog/authors/leighajarett.yaml @@ -0,0 +1,9 @@ +name: "Leigha Jarett" +username: "leighajarett" +bio: "Head of Product @FlutterFlow" +image: "12P-T3BTzLSSPrvomuK_oRQ.webp" +twitter: "LeighaJarett" +github: + handle: "leighajarett" + username: "Leigha Reid (Jarett)" + avatar_url: "https://avatars.githubusercontent.com/u/21065911?v=4" diff --git a/sites/www/content/blog/authors/liyuqian.yaml b/sites/www/content/blog/authors/liyuqian.yaml new file mode 100644 index 0000000000..f996c5a2b6 --- /dev/null +++ b/sites/www/content/blog/authors/liyuqian.yaml @@ -0,0 +1,6 @@ +name: "Yuqian Li" +username: "liyuqian" +bio: "Software engineer at Google dedicated to Flutter performance" +image: "1T1lv65-OBsGrBSs6mvgvqg.jpeg" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/lukeaf.yaml b/sites/www/content/blog/authors/lukeaf.yaml new file mode 100644 index 0000000000..b9826ed5bb --- /dev/null +++ b/sites/www/content/blog/authors/lukeaf.yaml @@ -0,0 +1,6 @@ +name: "Luke Freeman" +username: "lukeaf" +bio: "Maker of software and hardware. Cranky Australian. Founder @ Yakka (https://theyakka.com)." +image: "1osRrOD-rpUZlVPFwpn2_GQ.webp" +twitter: "lukeaf" +github: null diff --git a/sites/www/content/blog/authors/magder.yaml b/sites/www/content/blog/authors/magder.yaml new file mode 100644 index 0000000000..0162dddfa0 --- /dev/null +++ b/sites/www/content/blog/authors/magder.yaml @@ -0,0 +1,6 @@ +name: "Jenn Magder" +username: "magder" +bio: "Engineer at Google working on Flutter!" +image: "1N3hr8E92Mseta1S7yCX2Mg.webp" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/mariam_hasnany.yaml b/sites/www/content/blog/authors/mariam_hasnany.yaml new file mode 100644 index 0000000000..72a85b6c26 --- /dev/null +++ b/sites/www/content/blog/authors/mariam_hasnany.yaml @@ -0,0 +1,6 @@ +name: "Mariam Hasnany" +username: "mariam_hasnany" +bio: "product manager @Google #flutter |👩🏾‍🎓 UVA ’13 | 📚currently reading Fair Play by Eve Rodsky" +image: "1TDttyOKULZENb7QG-0Wl3w.jpeg" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/maryx.yaml b/sites/www/content/blog/authors/maryx.yaml new file mode 100644 index 0000000000..edb2b6de2f --- /dev/null +++ b/sites/www/content/blog/authors/maryx.yaml @@ -0,0 +1,6 @@ +name: "Mary" +username: "maryx" +bio: "" +image: "19Ub8P3iIx6Ji3hUUrSCZlg.webp" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/matthew-carroll.yaml b/sites/www/content/blog/authors/matthew-carroll.yaml new file mode 100644 index 0000000000..cb4f5554b1 --- /dev/null +++ b/sites/www/content/blog/authors/matthew-carroll.yaml @@ -0,0 +1,9 @@ +name: "Matt Carroll" +username: "mattcarroll" +bio: "I am SuperDeclarative! (Ex-Flutter at Google)" +image: "2j6fhR3Uq9ariRWaoB1MGPg.webp" +twitter: "SuprDeclarative" +github: + handle: "matthew-carroll" + username: "Matt Carroll" + avatar_url: "https://avatars.githubusercontent.com/u/7259036?v=4" diff --git a/sites/www/content/blog/authors/mehmetf.yaml b/sites/www/content/blog/authors/mehmetf.yaml new file mode 100644 index 0000000000..077a62d557 --- /dev/null +++ b/sites/www/content/blog/authors/mehmetf.yaml @@ -0,0 +1,9 @@ +name: "Mehmet Fidanboylu" +username: "mehmetf" +bio: "Googler since 2011. Flutter Evangelist." +image: "1snI23_LOekhWjI-XVYtHmQ.jpeg" +twitter: "" +github: + handle: "mehmetf" + username: "Mehmet Fidanboylu" + avatar_url: "https://avatars.githubusercontent.com/u/6687929?v=4" diff --git a/sites/www/content/blog/authors/melyndahoover.yaml b/sites/www/content/blog/authors/melyndahoover.yaml new file mode 100644 index 0000000000..378c134d7c --- /dev/null +++ b/sites/www/content/blog/authors/melyndahoover.yaml @@ -0,0 +1,6 @@ +name: "Mindy Hoover" +username: "melyndahoover" +bio: "I'm a UX researcher at Google working on improving the developer experience with Flutter" +image: "1NC_dwnZV11nnMuK4D0mXYA.jpeg" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/mhclin113.yaml b/sites/www/content/blog/authors/mhclin113.yaml new file mode 100644 index 0000000000..94bf213744 --- /dev/null +++ b/sites/www/content/blog/authors/mhclin113.yaml @@ -0,0 +1,6 @@ +name: "Cheng Lin" +username: "mhclin113" +bio: "Dual Product Manager at Flutter, Google & Video Solutions Consultant at gTech Ads, Google. Twitter: https://x.com/chenglinlim" +image: "1WKhF9u7jo7OkE26I5PjGiw.webp" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/mit-mit.yaml b/sites/www/content/blog/authors/mit-mit.yaml new file mode 100644 index 0000000000..3dee646005 --- /dev/null +++ b/sites/www/content/blog/authors/mit-mit.yaml @@ -0,0 +1,9 @@ +name: "Michael Thomsen" +username: "mit_mit" +bio: "Product Manager working on Dart and Flutter. Helping developers is my passion!" +image: "0Y_CFLc1qadgr3tPK.jpg" +twitter: "" +github: + handle: "mit-mit" + username: "Michael Thomsen" + avatar_url: "https://avatars.githubusercontent.com/u/13644170?v=4" diff --git a/sites/www/content/blog/authors/mjohnsullivan.yaml b/sites/www/content/blog/authors/mjohnsullivan.yaml new file mode 100644 index 0000000000..ab60e0cc39 --- /dev/null +++ b/sites/www/content/blog/authors/mjohnsullivan.yaml @@ -0,0 +1,9 @@ +name: "Matt Sullivan" +username: "mjohnsullivan" +bio: "" +image: "0cgL8XWJBpDSy2mHs.jpg" +twitter: "" +github: + handle: "mjohnsullivan" + username: "Matt Sullivan" + avatar_url: "https://avatars.githubusercontent.com/u/102488?v=4" diff --git a/sites/www/content/blog/authors/mravn.yaml b/sites/www/content/blog/authors/mravn.yaml new file mode 100644 index 0000000000..52a9dcc830 --- /dev/null +++ b/sites/www/content/blog/authors/mravn.yaml @@ -0,0 +1,9 @@ +name: "Mikkel Ravn" +username: "mravn" +bio: "Teaching IT at Business Academy Aarhus. Formerly at Chainalysis, JADBio, Google, QIAGEN, CLC bio, Systematic, U Aarhus. PhD in computer science 2003." +image: "1HcMDVJ2lxSQDClP1IUCGzg.jpeg" +twitter: "mnravn" +github: + handle: "mravn" + username: "Mikkel Nygaard Ravn" + avatar_url: "https://avatars.githubusercontent.com/u/6498924?v=4" diff --git a/sites/www/content/blog/authors/nikkitagandhi.yaml b/sites/www/content/blog/authors/nikkitagandhi.yaml new file mode 100644 index 0000000000..b28c6f1c5e --- /dev/null +++ b/sites/www/content/blog/authors/nikkitagandhi.yaml @@ -0,0 +1,6 @@ +name: "Nikita Gandhi" +username: "nikkitagandhi" +bio: "Community Manager at Google For Developers, Flutter Enthusiast, Avid Writer, Apathetic at Social Gatherings! Can make round dosas!" +image: "2G__H_oe3299HvlPy7YkpKg.webp" +twitter: "Nikkitagandhi" +github: null diff --git a/sites/www/content/blog/authors/parlough.yaml b/sites/www/content/blog/authors/parlough.yaml new file mode 100644 index 0000000000..b98bfb8d4d --- /dev/null +++ b/sites/www/content/blog/authors/parlough.yaml @@ -0,0 +1,9 @@ +name: "Parker Lougheed" +username: "parlough" +bio: "Find me on GitHub at https://github.com/parlough working on the Dart and Flutter developer and learning experience." +image: "1Yh3FMfH210BvJATo22X4Aw.jpeg" +twitter: "" +github: + handle: "parlough" + username: "Parker Lougheed" + avatar_url: "https://avatars.githubusercontent.com/u/18372958?v=4" diff --git a/sites/www/content/blog/authors/perclasson.yaml b/sites/www/content/blog/authors/perclasson.yaml new file mode 100644 index 0000000000..1bb322ca89 --- /dev/null +++ b/sites/www/content/blog/authors/perclasson.yaml @@ -0,0 +1,6 @@ +name: "Per Classon" +username: "perclasson" +bio: "Software Engineer 👨🏼‍💻" +image: "2d-UpSVU0saEV2On5JBvYaQ.jpeg" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/piinks.yaml b/sites/www/content/blog/authors/piinks.yaml new file mode 100644 index 0000000000..fd5b4db9a3 --- /dev/null +++ b/sites/www/content/blog/authors/piinks.yaml @@ -0,0 +1,9 @@ +name: "Kate Lovett" +username: "katelovett" +bio: "" +image: "00yA7OjxKWthRwjK2.webp" +twitter: "" +github: + handle: "Piinks" + username: "Kate Lovett" + avatar_url: "https://avatars.githubusercontent.com/u/16964204?v=4" diff --git a/sites/www/content/blog/authors/redbrogdon.yaml b/sites/www/content/blog/authors/redbrogdon.yaml new file mode 100644 index 0000000000..a8d50be6c4 --- /dev/null +++ b/sites/www/content/blog/authors/redbrogdon.yaml @@ -0,0 +1,9 @@ +name: "Andrew Brogdon" +username: "redbrogdon" +bio: "Engineer at Google, currently cranking out code for the Flutter developer relations team." +image: "0UfXdwppirgDYRNaj.webp" +twitter: "" +github: + handle: "redbrogdon" + username: "Andrew Brogdon" + avatar_url: "https://avatars.githubusercontent.com/u/969662?v=4" diff --git a/sites/www/content/blog/authors/rich_hall.yaml b/sites/www/content/blog/authors/rich_hall.yaml new file mode 100644 index 0000000000..2d5be8ae99 --- /dev/null +++ b/sites/www/content/blog/authors/rich_hall.yaml @@ -0,0 +1,6 @@ +name: "Rich Hall" +username: "rich_hall" +bio: "Search Ecosystem Consultant at Google and part time Flutter fanboy." +image: "1w7Y-bThACd0V1YJ6N7QePg.webp" +twitter: "rich_hall_" +github: null diff --git a/sites/www/content/blog/authors/scheglov.yaml b/sites/www/content/blog/authors/scheglov.yaml new file mode 100644 index 0000000000..ca2e3954d9 --- /dev/null +++ b/sites/www/content/blog/authors/scheglov.yaml @@ -0,0 +1,9 @@ +name: "Konstantin Scheglov" +username: "konstantin_scheglov" +bio: "Software Engineer @ Google" +image: "0m23OLfjD-pystWVb.jpg" +twitter: "" +github: + handle: "scheglov" + username: "Konstantin Scheglov" + avatar_url: "https://avatars.githubusercontent.com/u/384794?v=4" diff --git a/sites/www/content/blog/authors/sethladd.yaml b/sites/www/content/blog/authors/sethladd.yaml new file mode 100644 index 0000000000..28c0bcb6a5 --- /dev/null +++ b/sites/www/content/blog/authors/sethladd.yaml @@ -0,0 +1,9 @@ +name: "Seth Ladd" +username: "sethladd" +bio: "Product Manager @ Google for Fuchsia, author, conference organizer. Loves badminton and board games." +image: "10pSgo-h67AMv_gytSDEiJg.webp" +twitter: "sethladd" +github: + handle: "sethladd" + username: "Seth Ladd" + avatar_url: "https://avatars.githubusercontent.com/u/5479?v=4" diff --git a/sites/www/content/blog/authors/sfshaza2.yaml b/sites/www/content/blog/authors/sfshaza2.yaml new file mode 100644 index 0000000000..e53eff23f9 --- /dev/null +++ b/sites/www/content/blog/authors/sfshaza2.yaml @@ -0,0 +1,9 @@ +name: "Shams Zakhour" +username: "shaza" +bio: "I write Flutter docs." +image: "0LHz-9FgHd3doKgCM.jpg" +twitter: "" +github: + handle: "sfshaza2" + username: "Shams Zakhour" + avatar_url: "https://avatars.githubusercontent.com/u/44418985?v=4" diff --git a/sites/www/content/blog/authors/shamiramarshall.yaml b/sites/www/content/blog/authors/shamiramarshall.yaml new file mode 100644 index 0000000000..ca31dd4f56 --- /dev/null +++ b/sites/www/content/blog/authors/shamiramarshall.yaml @@ -0,0 +1,6 @@ +name: "Shamira Marshall" +username: "shamiramarshall" +bio: "Technical Writer at Google" +image: "0vHty7_58DD6eW53D.webp" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/swavkulinski.yaml b/sites/www/content/blog/authors/swavkulinski.yaml new file mode 100644 index 0000000000..cb535e59a3 --- /dev/null +++ b/sites/www/content/blog/authors/swavkulinski.yaml @@ -0,0 +1,9 @@ +name: "Swav Kulinski" +username: "swav_kulinski" +bio: "Computer enthusiast since 80s, Android developer since Eclair, now Flutter enthusiast. https://gitlab.com/swav-kulinski" +image: "0Qgrum-sTozr6lXCm.jpg" +twitter: "robotoasterdev" +github: + handle: "swavkulinski" + username: "Swav Kulinski (Robotoaster)" + avatar_url: "https://avatars.githubusercontent.com/u/2101969?v=4" diff --git a/sites/www/content/blog/authors/taodong.yaml b/sites/www/content/blog/authors/taodong.yaml new file mode 100644 index 0000000000..b119cdf0f9 --- /dev/null +++ b/sites/www/content/blog/authors/taodong.yaml @@ -0,0 +1,6 @@ +name: "Tao Dong" +username: "taodong" +bio: "I’m a HCI researcher and UX Lead focused on applying AI to the full lifecycle of software engineering. Personal website: https://www.taodong.net/." +image: "0jbYlQ-K9Yh-HVUBp.jpg" +twitter: "taodong" +github: null diff --git a/sites/www/content/blog/authors/theaflowers.yaml b/sites/www/content/blog/authors/theaflowers.yaml new file mode 100644 index 0000000000..40fe01f13d --- /dev/null +++ b/sites/www/content/blog/authors/theaflowers.yaml @@ -0,0 +1,6 @@ +name: "Alethea K. Flowers" +username: "theaflowers" +bio: "Flutterista & CircuitPythonista" +image: "0cNAKYVA08TeS6TVB.webp" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/tianguang.yaml b/sites/www/content/blog/authors/tianguang.yaml new file mode 100644 index 0000000000..dc39ef56d0 --- /dev/null +++ b/sites/www/content/blog/authors/tianguang.yaml @@ -0,0 +1,6 @@ +name: "Tianguang Zhang" +username: "tianguang" +bio: "" +image: "0xV6b-152TcEMSjUr.webp" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/timothyhoang.yaml b/sites/www/content/blog/authors/timothyhoang.yaml new file mode 100644 index 0000000000..3f18859513 --- /dev/null +++ b/sites/www/content/blog/authors/timothyhoang.yaml @@ -0,0 +1,6 @@ +name: "Timothy Hoang" +username: "timothyhoang" +bio: "Product Manager @ Google" +image: "1krC2R3xE1FCZTdIbXQDT_A.jpeg" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/timsneath.yaml b/sites/www/content/blog/authors/timsneath.yaml new file mode 100644 index 0000000000..a80515c81a --- /dev/null +++ b/sites/www/content/blog/authors/timsneath.yaml @@ -0,0 +1,9 @@ +name: "Tim Sneath" +username: "timsneath" +bio: "Director for Developer Tools and Frameworks at Apple. I used to run Flutter and Dart at Google." +image: "14HRhyWLOwtZEN7MQ8zLuHA.jpeg" +twitter: "timsneath" +github: + handle: "timsneath" + username: "Tim Sneath" + avatar_url: "https://avatars.githubusercontent.com/u/2319867?v=4" diff --git a/sites/www/content/blog/authors/tomayac.yaml b/sites/www/content/blog/authors/tomayac.yaml new file mode 100644 index 0000000000..ea02c721d4 --- /dev/null +++ b/sites/www/content/blog/authors/tomayac.yaml @@ -0,0 +1,9 @@ +name: "Thomas Steiner" +username: "tomayac" +bio: "Web Developer Advocate at Google" +image: "15Yq8VeVDQqM51HKi50phdg.jpeg" +twitter: "tomayac" +github: + handle: "tomayac" + username: "Thomas Steiner" + avatar_url: "https://avatars.githubusercontent.com/u/145676?v=4" diff --git a/sites/www/content/blog/authors/tvolkert.yaml b/sites/www/content/blog/authors/tvolkert.yaml new file mode 100644 index 0000000000..9c1b692fe4 --- /dev/null +++ b/sites/www/content/blog/authors/tvolkert.yaml @@ -0,0 +1,9 @@ +name: "Todd Volkert" +username: "tvolkert" +bio: "" +image: "0EUSBYlBzvqL9j_Xr.webp" +twitter: "" +github: + handle: "tvolkert" + username: "Todd Volkert" + avatar_url: "https://avatars.githubusercontent.com/u/15253456?v=4" diff --git a/sites/www/content/blog/authors/twerske.yaml b/sites/www/content/blog/authors/twerske.yaml new file mode 100644 index 0000000000..c203ccc1aa --- /dev/null +++ b/sites/www/content/blog/authors/twerske.yaml @@ -0,0 +1,9 @@ +name: "Emma Twersky" +username: "twerske" +bio: "" +image: "0lw25USWxDjT-i0kV.jpg" +twitter: "twerske" +github: + handle: "twerske" + username: "Emma Twersky" + avatar_url: "https://avatars.githubusercontent.com/u/15061394?v=4" diff --git a/sites/www/content/blog/authors/verygoodopensource.yaml b/sites/www/content/blog/authors/verygoodopensource.yaml new file mode 100644 index 0000000000..d21babad3f --- /dev/null +++ b/sites/www/content/blog/authors/verygoodopensource.yaml @@ -0,0 +1,9 @@ +name: "Very Good Ventures" +username: "vgv_team" +bio: "Very Good Ventures (https://verygood.ventures), a leading Flutter app development consultancy -- building successful experiences for all." +image: "1XUX_mTFnhm4BKyT9q9sOUQ.webp" +twitter: "VGVentures" +github: + handle: "VeryGoodOpenSource" + username: "Very Good Open Source" + avatar_url: "https://avatars.githubusercontent.com/u/74265434?v=4" diff --git a/sites/www/content/blog/authors/wmleler1.yaml b/sites/www/content/blog/authors/wmleler1.yaml new file mode 100644 index 0000000000..4f34366682 --- /dev/null +++ b/sites/www/content/blog/authors/wmleler1.yaml @@ -0,0 +1,6 @@ +name: "Wm Leler" +username: "wmleler1" +bio: "Computer scientist, musician, artist." +image: "0c8OAb9nT35XgY56T.jpg" +twitter: "" +github: null diff --git a/sites/www/content/blog/authors/xster.yaml b/sites/www/content/blog/authors/xster.yaml new file mode 100644 index 0000000000..79a3903680 --- /dev/null +++ b/sites/www/content/blog/authors/xster.yaml @@ -0,0 +1,6 @@ +name: "xster" +username: "xster" +bio: "" +image: "1h_lwkzhaAtW5WTkM750_Xw.jpeg" +twitter: "xsterx" +github: null diff --git a/sites/www/content/blog/authors/yanchenum.yaml b/sites/www/content/blog/authors/yanchenum.yaml new file mode 100644 index 0000000000..6d9b229540 --- /dev/null +++ b/sites/www/content/blog/authors/yanchenum.yaml @@ -0,0 +1,6 @@ +name: "Yan Chen" +username: "yanchenum" +bio: "A learner" +image: "0PSrFnoWOMMa0YZUM.jpg" +twitter: "yanchenum" +github: null diff --git a/sites/www/content/blog/authors/yegorj.yaml b/sites/www/content/blog/authors/yegorj.yaml new file mode 100644 index 0000000000..cbb66dadcb --- /dev/null +++ b/sites/www/content/blog/authors/yegorj.yaml @@ -0,0 +1,6 @@ +name: "Yegor Jbanov" +username: "yegorj" +bio: "Software Engineer @Google @Flutter, https://twitter.com/yegorjbanov" +image: "08pUBYXbTsN324CAB.jpg" +twitter: "yegorjbanov" +github: null diff --git a/sites/www/content/blog/authors/zoeyfan.yaml b/sites/www/content/blog/authors/zoeyfan.yaml new file mode 100644 index 0000000000..7e84697e80 --- /dev/null +++ b/sites/www/content/blog/authors/zoeyfan.yaml @@ -0,0 +1,6 @@ +name: "Zoey Fan" +username: "zoeyfan" +bio: "Senior Product Manager for Flutter, Google" +image: "2Cl8MkXie_Fr91CEmLGzQtw.jpeg" +twitter: "" +github: null diff --git a/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/136hoBmockzA11GwUI9bvHQ.webp b/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/136hoBmockzA11GwUI9bvHQ.webp new file mode 100644 index 0000000000..8123d2f64a Binary files /dev/null and b/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/136hoBmockzA11GwUI9bvHQ.webp differ diff --git a/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/1CB1OU6uH4SsgJUB2j962dg.webp b/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/1CB1OU6uH4SsgJUB2j962dg.webp new file mode 100644 index 0000000000..7c656c11ef Binary files /dev/null and b/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/1CB1OU6uH4SsgJUB2j962dg.webp differ diff --git a/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/1CMgf7ecD3j4TDGYFcYEJpg.webp b/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/1CMgf7ecD3j4TDGYFcYEJpg.webp new file mode 100644 index 0000000000..e1140795e6 Binary files /dev/null and b/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/1CMgf7ecD3j4TDGYFcYEJpg.webp differ diff --git a/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/1G5QWXo1DV1g_hy_Yj9gQGA.webp b/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/1G5QWXo1DV1g_hy_Yj9gQGA.webp new file mode 100644 index 0000000000..e49d5de99e Binary files /dev/null and b/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/1G5QWXo1DV1g_hy_Yj9gQGA.webp differ diff --git a/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/1GM0z7Rbgpig7gKVB7WCNeg.webp b/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/1GM0z7Rbgpig7gKVB7WCNeg.webp new file mode 100644 index 0000000000..9c377d780c Binary files /dev/null and b/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/1GM0z7Rbgpig7gKVB7WCNeg.webp differ diff --git a/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/1Ja5by3uzTNwQNKp3x-2gog.webp b/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/1Ja5by3uzTNwQNKp3x-2gog.webp new file mode 100644 index 0000000000..d01692dbd8 Binary files /dev/null and b/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/1Ja5by3uzTNwQNKp3x-2gog.webp differ diff --git a/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/1vGoaoEKT4ugjEJm2OvzvBQ.webp b/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/1vGoaoEKT4ugjEJm2OvzvBQ.webp new file mode 100644 index 0000000000..1a9374b85d Binary files /dev/null and b/sites/www/content/blog/beautiful-animated-charts-for-flutter/images/1vGoaoEKT4ugjEJm2OvzvBQ.webp differ diff --git a/sites/www/content/blog/beautiful-animated-charts-for-flutter/index.md b/sites/www/content/blog/beautiful-animated-charts-for-flutter/index.md new file mode 100644 index 0000000000..a17955aacf --- /dev/null +++ b/sites/www/content/blog/beautiful-animated-charts-for-flutter/index.md @@ -0,0 +1,169 @@ +--- +title: "Beautiful, animated charts for Flutter" +description: "We are super excited to announce a fully-featured, animated charting library for Flutter!" +publishDate: 2018-03-23 +author: mit-mit +image: images/1CB1OU6uH4SsgJUB2j962dg.webp +category: announcements +layout: blog +--- + +*Flutter enables developers to craft high-quality mobile app user interfaces for iOS and Android. Many of the Flutter apps being created internally at Google present large amounts of data. Doing that efficiently requires not just regular UI widgets, but lots of elegant charts. Back when we set out to create a Flutter charting library we quickly discovered that [it was no easy feat](https://medium.com/flutter-io/zero-to-one-with-flutter-43b13fd7b354). So it is with much joy that we are now able to announce the ‘Charts for Flutter’-library [as open source](https://github.com/google/charts), available for all Flutter apps. Checkout [`charts_flutter`](https://pub.dartlang.org/packages/charts_flutter)!* + +## The need for animation + +One of the core complexities we discovered early was that great charts don’t just display data statically. For them to be engaging they need to be “alive”. For example, to establish page transitions they need to animate in, and when new data is loaded they need to gradually animate to the new state so it’s clear to the user what the changes are: + +<DashImage figure src="images/1GM0z7Rbgpig7gKVB7WCNeg.webp" alt="Using animation to gradually bring in changes to the dataset" caption="Using animation to gradually bring in changes to the dataset" /> + + +Building animated charts is not as simple as it may seem. For a “behind the curtain” tale, check out Mikkel’s great post on the engineering challenge our team tackled when [we built the initial library](https://medium.com/flutter-io/zero-to-one-with-flutter-43b13fd7b354). + +## Supported chart types + +Charts for Flutter supports three chart types, each with several configuration options: + +* **Bar** charts, incl. support for stacking several data series, showing target lines, and rendering the bars horizontally + +* **Line** charts, including support for time series + +* **Pie** and ‘**donut**’ charts + +Here are a few sample charts created with Charts for Flutter: + +<DashImage figure src="images/1CB1OU6uH4SsgJUB2j962dg.webp" alt="Bar chart with three grouped series" caption="Bar chart with three grouped series" /> + + +<DashImage figure src="images/1CMgf7ecD3j4TDGYFcYEJpg.webp" alt="Bar chart with grouped and stacked bars" caption="Bar chart with grouped and stacked bars" /> + + +<DashImage figure src="images/1G5QWXo1DV1g_hy_Yj9gQGA.webp" alt="Donut chart" caption="Donut chart" /> + + +<DashImage figure src="images/1Ja5by3uzTNwQNKp3x-2gog.webp" alt="Line chart with custom dash-line patterns" caption="Line chart with custom dash-line patterns" /> + + +<DashImage figure src="images/1vGoaoEKT4ugjEJm2OvzvBQ.webp" alt="Bar chart with custom legend using symbols" caption="Bar chart with custom legend using symbols" /> + + +For more details, see the Charts Gallery app located inside the `/example/` folder of the repo, and in the [online gallery](https://google.github.io/charts/flutter/gallery). + +## Getting started with Charts for Flutter + +It’s pretty easy to use Charts for Flutter! We’ll start by [creating](https://flutter.io/get-started/test-drive/) a new Flutter app, and then add a bar chart to that to visualize the number of clicks: two bars visualizing the past number of clicks (fixed values), and one bar for the current year (tied to the current state of the `_counter` variable): + +<DashImage figure src="images/136hoBmockzA11GwUI9bvHQ.webp" alt="The Flutter counter app extended with a bar chart" caption="The Flutter counter app extended with a bar chart" /> + + +Start by declaring a dependency on the package: Open the `pubspec.yaml` file, and add an entry for `charts_flutter` in the dependencies section. + +``` +dependencies: + flutter: + sdk: flutter + charts_flutter: ^0.2.0 +``` + + +Now run your IDE’s function to get packages, or run `flutter packages get`from the terminal. + +Next, define a data type to hold the data for each bar column. Since we want years on the x-axis and counts on the y-axis, we create a class with a `String` and an `int` field: + +```dart +class ClicksPerYear { + final String year; + final int clicks; + + ClicksPerYear(this.year, this.clicks); +} +``` + +Then, to support specifying a color for each year, we add a charts `color` field, which we convert from Flutter’s corresponding `Color` type: + +```dart +class ClicksPerYear { + final String year; + final int clicks; + final charts.Color color; + + ClicksPerYear(this.year, this.clicks, Color color) + : this.color = new charts.Color( + r: color.red, g: color.green, b: color.blue, a: color.alpha); +} +``` + +Next, we will update the `build()` method of `_MyHomePageState`to create the chart data set, using the `_counter` variable: + +```dart +var data = [ + new ClicksPerYear('2016', 12, Colors.red), + new ClicksPerYear('2017', 42, Colors.yellow), + new ClicksPerYear('2018', _counter, Colors.green), +]; +``` + +Next, we need to wrap data in a Series. Let’s review a few core charting concepts first: + +* **Domain**: The thing being measured; for example, *‘type of vehicle’* + +* **Measure:** The numerical value observed; for example, *‘number of wheels’* + +* **Data point**: A measure for a domain; for example, *(‘car’, 4).* Sometimes referred to as a *datum*. + +* **Series**: A sequenced collection of individual data points; for example *[(‘car’, 4), (‘bicycle’, 2)]* + +* **Id**: A unique identifier for a single series; used for charts that render several series; for example ‘*Typical number of wheels’,* and *‘Maximum number of wheels’* + +With that we can create our data series, setting the `year` component of our data class to the domain, the `clicks` component to the measure: + +```dart +var series = [ + new charts.Series( + id: 'Clicks', + domainFn: (ClicksPerYear clickData, _) => clickData.year, + measureFn: (ClicksPerYear clickData, _) => clickData.clicks, + colorFn: (ClicksPerYear clickData, _) => clickData.color, + data: data, + ), +]; +``` + +And then we are ready to create the chart; to control its padding and size we wrap it in `Padding` and `SizedBox`: + +```dart +var chart = new charts.BarChart<ClicksPerYear>( + series, + animate: true, +); +var chartWidget = new Padding( + padding: new EdgeInsets.all(32.0), + child: new SizedBox( + height: 200.0, + child: chart, + ), +); +``` + +And then finally we can add the chart below the two text labels: + +```dart +child: new Column( + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + new Text( + 'You have pushed the button this many times:', + ), + new Text( + '$_counter', + style: Theme.of(context).textTheme.display1, + ), + chartWidget, + ], +), +``` + +Presto, a nice bar chart! + +The [complete code](https://gist.github.com/mit-mit/08168a773a56bb58c7abfd9b8e82f467) is also available for review. + +We look forward to seeing what you might build with [Charts for Flutter](https://pub.dartlang.org/packages/charts_flutter). Should you have any suggestions, or find any bugs, please [file an issue](https://github.com/google/charts/issues) in our tracker! Should Charts for Flutter not match your requirements, you may also want to take a look at some of the other charting packages in [Pub](https://pub.dartlang.org/flutter/packages?q=chart), such as [flutter_circular_chart](https://pub.dartlang.org/packages/flutter_circular_chart), [flutter_charts](https://pub.dartlang.org/packages/flutter_charts), and [flutter_sparkline](https://pub.dartlang.org/packages/flutter_sparkline). \ No newline at end of file diff --git a/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/01Qz7x7ZMo0Ar75hc.webp b/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/01Qz7x7ZMo0Ar75hc.webp new file mode 100644 index 0000000000..f847e9ee9f Binary files /dev/null and b/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/01Qz7x7ZMo0Ar75hc.webp differ diff --git a/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/05e8cSvZaG83--mC7.webp b/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/05e8cSvZaG83--mC7.webp new file mode 100644 index 0000000000..760e451854 Binary files /dev/null and b/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/05e8cSvZaG83--mC7.webp differ diff --git a/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/0KQvECPhOz7KPP1k2.webp b/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/0KQvECPhOz7KPP1k2.webp new file mode 100644 index 0000000000..e1bae17d32 Binary files /dev/null and b/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/0KQvECPhOz7KPP1k2.webp differ diff --git a/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/0RWD-5EEHxQ1aCCwi.webp b/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/0RWD-5EEHxQ1aCCwi.webp new file mode 100644 index 0000000000..d0cafe9d17 Binary files /dev/null and b/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/0RWD-5EEHxQ1aCCwi.webp differ diff --git a/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/0WBnCrRnxqRMnFYdf.webp b/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/0WBnCrRnxqRMnFYdf.webp new file mode 100644 index 0000000000..a0eace54db Binary files /dev/null and b/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/0WBnCrRnxqRMnFYdf.webp differ diff --git a/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/0h4OqTkRAx1_-RQiw.webp b/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/0h4OqTkRAx1_-RQiw.webp new file mode 100644 index 0000000000..0d428a9c8f Binary files /dev/null and b/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/0h4OqTkRAx1_-RQiw.webp differ diff --git a/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/0r3sTKix-QiZV6vrB.webp b/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/0r3sTKix-QiZV6vrB.webp new file mode 100644 index 0000000000..c405c5818c Binary files /dev/null and b/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/0r3sTKix-QiZV6vrB.webp differ diff --git a/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/1BxmLRsMw0ZSPWXLF0JTtDQ.webp b/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/1BxmLRsMw0ZSPWXLF0JTtDQ.webp new file mode 100644 index 0000000000..540d014b17 Binary files /dev/null and b/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/images/1BxmLRsMw0ZSPWXLF0JTtDQ.webp differ diff --git a/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/index.md b/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/index.md new file mode 100644 index 0000000000..5b9996e115 --- /dev/null +++ b/sites/www/content/blog/best-practices-for-hosting-a-live-streaming-coding-session/index.md @@ -0,0 +1,124 @@ +--- +title: "Best practices for hosting a live streaming coding session" +description: "Live streaming has become an emerging practice for knowledge sharing in developer communities, where developers broadcast their work to…" +publishDate: 2019-10-28 +author: yanchenum +image: images/05e8cSvZaG83--mC7.webp +category: spotlight +layout: blog +--- + +<DashImage figure src="images/05e8cSvZaG83--mC7.webp" alt="(credit: [https://www.youtube.com/watch?v=YYP5lqgno4k](https://www.youtube.com/watch?v=YYP5lqgno4k))" caption="(credit: [https://www.youtube.com/watch?v=YYP5lqgno4k](https://www.youtube.com/watch?v=YYP5lqgno4k))" /> + + +Live streaming has become an emerging practice for knowledge sharing in developer communities, where developers broadcast their work to live audiences on streaming platforms such as YouTube. As a summer intern with Google’s Flutter team, I conducted an interview study to understand why and how developers share programming knowledge via live streaming. In this article, I would like to share some of the best practices I found for hosting a live streamed coding session, so that *you* can successfully create your own and share programming knowledge with your community. I would encourage anyone to try live streaming because not only can your livestream help others, but you, the streamer, can get real time feedback from your audience as well. + +## Interviewing streamers and viewers + +I interviewed 20 people, 14 streamers and 6 viewers, who regularly participated in programming live streams on YouTube or Twitch. The streamers we interviewed all reported that sharing programming knowledge and practical skills was the primary purpose of hosting live streams. In addition, we selected viewers who typically watch livestream for at least 30 minutes at a time to make sure they have an informed opinion about the topic. + +<DashImage figure src="images/0RWD-5EEHxQ1aCCwi.webp" /> + + +The people I interviewed used different kinds of programming languages including JavaScript (8), Python (4), C# (3), Dart (2), Rust (2), and C (1). Out of the 20 interviewees, 14 were professional developers who were paid to develop software, 4 were students, and 2 were hobbyist programmers. Out of the 14 streamers, 7 of them primarily streamed about themselves working through programming tasks that are related to open source projects. + +## 9 tips for live streaming session a coding session + +I learned many best practices to follow, and pitfalls to avoid from speaking with experienced streamers and viewers in the developer community. I’d like to share 9 tips with you, broken up into three sections: before the livestream, during the livestream, and after the livestream. + +## ***Before Stream*** + +### 1. Find a topic of interest to your community + +Some streamers chose stream topics based on the interests of their communities. This includes discussion in Discord or Slack channels, comments from their previous videos, or poll results from prior streams. Depending on your comfort level, you can proactively ask people for their topics of interest and invite them to watch your stream. + +### 2. Make multiple announcements the week before + +Most streamers keep a regular streaming time (for example, Sunday 10 a.m. — 12 p.m. EST), which is recommended if you want your viewers to make your session every week. However, if you don’t have a regular streaming time, or you want to change the time you stream this week, make sure you remind your viewers multiple times (for example, at 5 days, 2 days, and the day before you go live) when your streaming session is happening. + +<DashImage figure src="images/0WBnCrRnxqRMnFYdf.webp" /> + + +### 3. Protect your privacy + +All streamers have concerns regarding their privacy. This could include mistakenly sharing your personal information, or your organization’s business information. Here is a list of practices that experienced streamers follow to protect their privacy: + +* Use incognito mode and hide your bookmarks + +<DashImage figure src="images/1BxmLRsMw0ZSPWXLF0JTtDQ.webp" /> + + +* Share only the relevant part of your screen + +* Before the stream, log into all of the accounts that you will be using in your stream (for example, GitHub, Discord), so you won’t accidentally show your username/password. + +* Turn off your notifications (such as email and messenger) + +* Don’t face your camera to anything that is publicly recognizable (such as buildings with a unique shape) + +### 4. Create a FAQ and resources list + +Some viewers will join in the middle of your stream, and they might be confused about what you’re working on. Instead of getting distracted answering questions, prepare a list of questions you anticipate the audience might ask. For example: + +*Q: What is this stream about?* + +*A: We’re building a Hacker News Reader app in Flutter!* + +*Q: Where is the GitHub repo?* + +*A: [https://goo.gle/2MXeNeR](https://goo.gle/2MXeNeR)* + +*Q: Which IDE are you using in the stream?* + +*A: IntelliJ.* + +### 5. Have a high level plan + +Although the prep work for live streaming is much less than for recording a video, you may still want to have a high level plan. This could include writing down the steps of what you want to accomplish, which can help you stay focused. + +<DashImage figure src="images/0r3sTKix-QiZV6vrB.webp" alt="(credit: [https://unsplash.com/photos/RLw-UC03Gwc](https://unsplash.com/photos/RLw-UC03Gwc))" caption="(credit: [https://unsplash.com/photos/RLw-UC03Gwc](https://unsplash.com/photos/RLw-UC03Gwc))" /> + + +### 6. No need to complete the work before hand +> *“It’s not really about me trying to solve a problem. It’s just me trying to learn more or see someone else coding.” — A viewer* + +Several viewers reported that when deciding to watch live streams they do not have specific problems to solve or concrete learning objectives. This means that you don’t have to work everything out beforehand. Viewers like to see what kinds of issues you will run into and how you address them. For example, here is what one viewer said in our study: +> *“I like to see how the person thinks and makes mistakes, and a lot of people edit it too harshly. And then it ends up being this one fluid front-to-back program with no mistakes. They are not researching anything or looking anything up, and that’s not realistic to me. So I think it’s a bit more entertaining, and a bit easier to sit through a lot of the live stream videos or even their arcade videos when they leave in the mistakes.” — A viewer* + +## ***During Stream*** + +Great! You’ve done all your preparation, and now you’re on-air! Viewers watch live streams because they want to understand the streamer’s thinking process, debugging approach, and why they choose one strategy versus another. That brings us to the next tip: + +### **7. Keep your viewers engaged** + +There are two techniques to get your viewers engaged. + +The first one is narrating your thoughts *—* you can ask your viewers to remind you to do this if you find it difficult. + +<DashImage figure src="images/0h4OqTkRAx1_-RQiw.webp" alt="(credit: [https://unsplash.com/photos/ASKeuOZqhYU](https://unsplash.com/photos/ASKeuOZqhYU))" caption="(credit: [https://unsplash.com/photos/ASKeuOZqhYU](https://unsplash.com/photos/ASKeuOZqhYU))" /> + +> *“I ask questions either about things I don’t know or things that I have a choice where I can implement either one thing or the other.” — A streamer* + +The second way is to ask viewers to provide input. For example, having a short Q&A session, or asking them to suggest what to do next using a poll. You can even ask your viewers for help if you get stuck coding. You don’t have to know everything, and they want to see you succeed! + +<DashImage figure src="images/01Qz7x7ZMo0Ar75hc.webp" alt="(credit: [https://unsplash.com/photos/YLUvemTiRtk](https://unsplash.com/photos/YLUvemTiRtk))" caption="(credit: [https://unsplash.com/photos/YLUvemTiRtk](https://unsplash.com/photos/YLUvemTiRtk))" /> + + +### **8. Chat moderation** + +Once your audience grows large, ask some trustworthy viewers to help you moderate the chat so that you can focus on coding. To grow a healthy community, make sure you address issues such as harassment, spam, and trolling as soon as you see them. + +## ***After stream*** + +Congratulations! You’ve had a successful live streaming session. Now you can upload your video to YouTube to share it with a broader audience. That brings us to the last tip: + +### **9. Create a timetable if you have time** + +Because a live stream recording is often long, viewers appreciate a Timestamp table to jump from topic to topic. The Flutter Boring Show provides a good example of such a table. + +<DashImage figure src="images/0KQvECPhOz7KPP1k2.webp" alt="(link: [https://youtu.be/SJKrtx759Xk)](https://youtu.be/SJKrtx759Xk))" caption="(link: [https://youtu.be/SJKrtx759Xk)](https://youtu.be/SJKrtx759Xk))" /> + + +## Ready to stream? + +Are you ready to go out and host a fantastic live stream about Flutter or any other programming tools you want to share? I hope the tips I introduced above will come in handy! \ No newline at end of file diff --git a/sites/www/content/blog/best-practices-for-optimizing-flutter-web-loading-speed/images/0_bz3_DRzn9aoB0qD.webp b/sites/www/content/blog/best-practices-for-optimizing-flutter-web-loading-speed/images/0_bz3_DRzn9aoB0qD.webp new file mode 100644 index 0000000000..b34bb6f8e7 Binary files /dev/null and b/sites/www/content/blog/best-practices-for-optimizing-flutter-web-loading-speed/images/0_bz3_DRzn9aoB0qD.webp differ diff --git a/sites/www/content/blog/best-practices-for-optimizing-flutter-web-loading-speed/index.md b/sites/www/content/blog/best-practices-for-optimizing-flutter-web-loading-speed/index.md new file mode 100644 index 0000000000..d1f84cb1d1 --- /dev/null +++ b/sites/www/content/blog/best-practices-for-optimizing-flutter-web-loading-speed/index.md @@ -0,0 +1,173 @@ +--- +title: "Best practices for optimizing Flutter web loading speed" +description: "As a Flutter developer working with the Google Flutter team and personally in my private time, I encounter and understand the concerns…" +publishDate: 2024-05-06 +author: mhclin113 +image: images/0_bz3_DRzn9aoB0qD.webp +category: tutorial +layout: blog +--- + +As a Flutter developer working with the Google Flutter team and personally in my private time, I encounter and understand the concerns regarding the loading speed of Flutter web apps. Optimizing loading speed is crucial for better performance and a positive user experience, especially as Flutter developers expand into web development. This guide offers actionable strategies and best practices to accelerate the performance of your Flutter web apps. + +## Rendering + +CanvasKit, the default [renderer](https://docs.flutter.dev/platform-integration/web/renderers) for Flutter web applications, offers high performance and pixel-perfect consistency across platforms by leveraging WebGL. This capability is particularly beneficial for complex graphical applications that require rich animations and high fidelity. However, the inherent file size of CanvasKit (around 1.5 MB) can be a drawback, especially for apps where initial load time is critical. + +Although the `flutter.js` load API parallelizes the download of CanvasKit and `main.dart.js`, all Flutter widgets have to wait for them to be fully loaded into the browser, potentially leading to noticeable delays before the application becomes interactive. To mitigate these concerns and optimize the loading experience, developers can choose the Wasm rendering mode. + +As [WebAssembly support](https://docs.flutter.dev/platform-integration/web/wasm) in Flutter web is considered experimental and subject to change, these steps are for developers willing to experiment with cutting-edge features. Features and commands might evolve, so always refer to the latest Flutter documentation for current practices. + +### Compatibility + +The `dart:html` package is not supported when building with Wasm. This limitation means you must carefully consider the APIs on which your app depends. Alternatively, the [web package](https://pub.dev/packages/web) is supported by both dart2wasm and dart2js. + +### Performance + +Wasm not only reduces app size compared to CanvasKit, it’s also faster to start up compared to JavaScript. + +## Lazy loading + +Dart’s [deferred imports](https://dart.dev/language/libraries#lazily-loading-a-library) allow you to split your code and load parts of it only when needed, reducing the initial load time. The following sections discuss how you can use deferred loading. + +### Declare a deferred import + +At the top of your Dart file, declare the import that you want to defer. In your import statement, specify `deferred as` followed by an identifier. When you need to use the library, load it asynchronously using the `loadLibrary()` method on the deferred import: + +```dart +import 'package:myapp/hello.dart' deferred as hello; + +Future<void> loadHelloLibrary() async { + await hello.loadLibrary(); + hello.sayHi(); +} +``` + + +### Call the load function + +In your Flutter app, call this function when needed, for example, as a response to user interactions. The following example loads the needed library when the user presses a widget: + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(MaterialApp(home: MyApp())); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: ElevatedButton( + onPressed: () { + loadHelloLibrary(); + }, + child: Text('Load Feature'), + ), + ), + ); + } +} +``` + + +## Unawaited function calls + +In order to reduce the time taken to display the initial widget of your app, try to not `await` for costly futures before calling `runApp`. Some futures can be left un-awaited so they update the UI after they complete. The [`unawaited` function](https://api.flutter.dev/flutter/dart-async/unawaited.html) allows app programmers to explicitly tell the “unawaited futures” lint that those futures are not expected to be awaited. This improvement enhances the user experience during both app startup and page loading by making the app feel more responsive. However, it’s important to manage such functions carefully to avoid issues related to app state consistency and resource management. + +```dart +import 'dart:async'; +import 'package:flutter/material.dart'; + +void main() { + unawaited(downloadVideos().then(videos) { + playlist.add(videos); + }); + + runApp(const MyApp()); +} +``` + + +## Media files + +### Displaying assets at optimal resolution + +Flutter automatically handles [loading assets at the appropriate resolution based on the device’s pixel density](https://docs.flutter.dev/ui/assets/assets-and-images#resolution-aware). This ensures optimal visuals across different screen sizes. While it’s important to optimize assets for efficient delivery, prioritize providing assets at the exact resolutions needed before exploring alternative image formats, as we’ll talk about in the next section. + +### Better image compression + +PNG and JPG are among the most prevalent image formats used on websites. These formats are renowned for their widespread support and compatibility. However, emerging next-generation formats like [WebP](https://developers.google.com/speed/webp) and [AVIF](https://netflixtechblog.com/avif-for-next-generation-image-coding-b1d75675fe4) offer significant advancements in reducing file sizes without substantially compromising image quality. For instance, a PNG image with an original size of 319 KB can be compressed to just 38 KB in WebP format or, even more impressively, to 10 KB in AVIF format. These reductions in file size are achieved with minimal perceptible loss in quality to the human eye, demonstrating the potential of these formats to enhance website loading speeds while maintaining visual fidelity. + +<DashImage figure src="images/0_bz3_DRzn9aoB0qD.webp" alt="PNG 319 KB / WebP 38 KB / AVIF 10 KB" caption="PNG 319 KB / WebP 38 KB / AVIF 10 KB" /> + + +However, it’s important to note that not all browsers support [WebP](https://caniuse.com/webp) and [AVIF](https://caniuse.com/avif) images. Before integrating these formats into your website, verify their compatibility with the browsers your audience uses most frequently. This will help you determine whether these next-generation image formats align with your website’s requirements and audience needs. + +## Caching + +### Memory, disk, service workers cache + +Leveraging the capabilities of memory cache, disk cache, and Service Workers can significantly reduce loading times after the initial page load. This is because these caching mechanisms require files to be loaded once before they can cache them. Memory cache, stored in RAM, offers rapid access speeds but is volatile. On the other hand, disk cache, although slower, provides persistence. Service Workers act as programmable network proxies, enabling sophisticated caching strategies across both memory and disk. + +Browsers or operating systems usually manage memory and disk caches automatically, eliminating the need for manual intervention unless there’s a specific requirement to manipulate them programmatically. While Flutter manages Service Workers to some extent, developers have the flexibility to implement custom Service Workers alongside Flutter for enhanced control over caching and network interactions. + +### Wasm + +Browsers cache Wasm files (like CanvasKit, and soon dart2wasm output) as well as their compiled native code. This means cached Wasm modules load as quickly as native binaries, unlike JavaScript, which requires reparsing, recompilation, and reJIT (Just-In-Time) processing. + +While Flutter’s Wasm build option isn’t fully stable yet, adopting modern JS-interop practices benefit you when dart2wasm stabilizes. For example, avoid using legacy libraries like `dart:html` and `dart:js`, and prefer `package:web` and `dart:js_interop`. Also, consider checking whether other packages you are using are compatible with Wasm. + +## Preloading + +### HTML <head tag>, HTTP response headers + +[Preloading assets](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/preload) like images, fonts, and JavaScript files can significantly improve webpage loading speed. By preloading within the HTML `<head>` tag or using HTTP response headers, you instruct the browser to download these resources before they’re needed for rendering. This eliminates delays and ensures a smoother user experience. To preload assets, add the `<link>` tag in the `<head>` section and set the `rel` attribute to `preload`. Only preload assets that are used immediately, optimally in the first screen of your app, otherwise browsers will consider the preloading as a waste of bandwidth. + +### HTML <head> tag + +```xml +<head> + <link rel="preload" href="assets/logo.webp" as="image" type="image/webp" /> +</head> +<body> + <picture> + <source src="assets/logo.webp" type="image/webp" /> + </picture> +</body> +``` + + +### HTTP response headers for Firebase hosting + +The following code block is a `firebase.json` example with a key/value combo demonstrating how to add HTTP headers for asset preloading. + +```json +"headers": [ + { + "key": "Link", + "value": "<assets/logo.webp>; rel=preload; as=image" + } +] +``` + + +### Landing page + +Flutter empowers you to build fully interactive landing pages for your app using plain HTML/CSS. While users engage with your landing page, `flutter.js` preloads your Flutter app — ensuring instant launches when the user navigates to the Flutter app. This is especially beneficial for games with a **Play** button and apps that require logging in. + +## Loading/splash screen + +While we’ve focused on technical optimizations for app loading speed, perceived loading speed is more critical. Your goal should be to make your app *feel fast*. + +Loading/splash screens are highly effective in enhancing this perception. By providing visual activity, they reassure users that the app is launching promptly. In contrast, a blank screen breeds uncertainty, potentially leading to frustration and page refreshes. + +For the fastest responsiveness, implement your splash screen directly in your `index.html` file using plain CSS/HTML. This minimizes any potential delays. + +For an example, check out the [Flutter Gallery implementation](https://github.com/flutter/gallery/blob/cfcb9dbda56697fe8bafe4b64c1a9261dde908ae/web/index.html#L211). + +## Conclusion + +In this doc, we’ve explored ways to accelerate both the initial loading and rendering performance of your Flutter web app. There are various strategies you can employ, but remember that each solution involves trade-offs. Choose the optimizations that best suit your specific needs and those of your users. By combining these approaches, you’ll create a smoother, more responsive user experience for your Flutter web apps. \ No newline at end of file diff --git a/sites/www/content/blog/black-lives-matter/images/0-1m66Jxnec3s8Qnb.webp b/sites/www/content/blog/black-lives-matter/images/0-1m66Jxnec3s8Qnb.webp new file mode 100644 index 0000000000..e2a37ac271 Binary files /dev/null and b/sites/www/content/blog/black-lives-matter/images/0-1m66Jxnec3s8Qnb.webp differ diff --git a/sites/www/content/blog/black-lives-matter/index.md b/sites/www/content/blog/black-lives-matter/index.md new file mode 100644 index 0000000000..ec7a93ef20 --- /dev/null +++ b/sites/www/content/blog/black-lives-matter/index.md @@ -0,0 +1,17 @@ +--- +title: "Black Lives Matter" +description: "We aren’t posting an article this week." +publishDate: 2020-06-03 +author: timsneath +layout: blog +--- + +We aren’t posting a #FlutterGoodNewsWednesday article this week. + +Here on the Flutter and Dart teams, we want to take this moment to stand with the Black community and tell you: we stand with you, we see you and we support you. This week’s events in the USA are a painful reminder that systemic racism remains prevalent and that justice is distributed unevenly in our society. + +We know that these are not issues that are solved with a few words; we need to engage individually and corporately. We also acknowledge that we’re part of the problem, and that many of us have work to do to examine our biases and privilege. + +We’re encouraging our teams to take time this week to learn and reflect on how we can be better allies and better foster diversity in our own community. Some of us are reading books like [The New Jim Crow](https://newjimcrow.com/) and watching documentaries like [13th](https://www.netflix.com/title/80091741); we have also found these guides to be helpful: [White Guyde To The Galaxy](https://tatianamac.com/posts/white-guyde/) and [Save the Tears: White Woman’s Guide](https://tatianamac.com/posts/save-the-tears). + +We’ll be quiet for the rest of this week and focus on amplifying [voices of color](https://www.linkedin.com/pulse/get-wrong-me-what-i-need-from-allies-megan-carpenter/?trackingId=xUb18B1MS1WaBOTqkw9xgA%3D%3D). \ No newline at end of file diff --git a/sites/www/content/blog/bringing-flutter-to-the-web/images/1D5HYFARy1U-T4E9OtK3LMA.webp b/sites/www/content/blog/bringing-flutter-to-the-web/images/1D5HYFARy1U-T4E9OtK3LMA.webp new file mode 100644 index 0000000000..1269f3e62d Binary files /dev/null and b/sites/www/content/blog/bringing-flutter-to-the-web/images/1D5HYFARy1U-T4E9OtK3LMA.webp differ diff --git a/sites/www/content/blog/bringing-flutter-to-the-web/images/1jQBkI-cZwOP_KAEnPhzDtg.webp b/sites/www/content/blog/bringing-flutter-to-the-web/images/1jQBkI-cZwOP_KAEnPhzDtg.webp new file mode 100644 index 0000000000..5ebb83a213 Binary files /dev/null and b/sites/www/content/blog/bringing-flutter-to-the-web/images/1jQBkI-cZwOP_KAEnPhzDtg.webp differ diff --git a/sites/www/content/blog/bringing-flutter-to-the-web/index.md b/sites/www/content/blog/bringing-flutter-to-the-web/index.md new file mode 100644 index 0000000000..db17c2983f --- /dev/null +++ b/sites/www/content/blog/bringing-flutter-to-the-web/index.md @@ -0,0 +1,63 @@ +--- +title: "Bringing Flutter to the Web" +description: "Flutter, our portable UI framework for building beautiful, tailored experiences, now supports the modern web! We’re pleased to release a…" +publishDate: 2019-05-07 +author: kevmoo +image: images/1jQBkI-cZwOP_KAEnPhzDtg.webp +category: announcements +layout: blog +--- + +[Flutter](https://flutter.dev), our portable UI framework for building beautiful, tailored experiences, now supports the modern web! We’re pleased to release a technical preview of the SDK that lets you take Flutter UI code and business logic and run it directly in the browser. + +## Our ambitions for Flutter on web browsers + +Since its first beta last year, customers have been using Flutter to create mobile apps that run on iOS and Android. However, Flutter was always architected as a portable UI toolkit and, among other places, runs on Windows, Mac, Fuchsia, and even Raspberry Pi. Since Flutter is built using Dart, which includes production-quality compilers for both native machine code and JavaScript, we had a solid foundation. The remaining challenge was to replace the Skia-based graphics engine and text renderer with web-platform equivalents. + +To do this right, we needed to deliver: + +* Fast, jank-free 60 frames-per-second performance for interactive graphics. + +* Consistent behavior and visuals with Flutter on other platforms. + +* High-productivity developer tooling that integrates with existing development patterns. + +* Support for core web features across all modern browsers. + +While Flutter for the web is a work in progress — and there’s plenty of work to do in each of these areas — we’re ready to share a preview so you can try it out and give us feedback. + +## Flutter web architecture + +The overall architecture of Flutter on the web closely resembles Flutter on mobile: + +<DashImage figure src="images/1D5HYFARy1U-T4E9OtK3LMA.webp" alt="Flutter for web architecture" caption="Flutter for web architecture" /> + + +The Flutter framework (green in the diagram above) is shared between the mobile and web offerings. It provides high-level abstractions for the UI foundations of Flutter, including animation, gestures, base widget classes, as well as a Material-themed set of widgets for most common application needs. If you’re already building on Flutter, you will feel immediately at home with Flutter for the web. + +The magic comes in translating these concepts to the browser. We’ve reimplemented the dart:ui library, replacing the bindings to the Skia engine used by mobile, with code that targets the DOM and Canvas. When you compile Flutter code for the web, your application, the Flutter framework, and the web-flavored dart:ui library — all written in Dart — are compiled to JavaScript that can run on any modern browser. + +<DashImage figure src="images/1jQBkI-cZwOP_KAEnPhzDtg.webp" alt="Flutter for web demo running on Chrome, Firefox, and Safari" caption="Flutter for web demo running on Chrome, Firefox, and Safari" /> + + +We are thoughtfully embracing core web features, like seamless integration of browser history with Flutter’s routing model. We are also coordinating with [Flutter’s desktop effort](https://github.com/flutter/flutter/wiki/Desktop-shells) to enable mouse scrolling, hover, and focus — features that not required in mobile experiences. + +The Flutter web project is focusing on the core value provided by the framework: rich, interactive experiences. Web content focused on documents can still benefit by embedding Flutter web visualizations. + +The preview framework exists as a temporary fork of the existing Flutter framework. This allowed our engineers to move quickly to implement web functionality while letting the core team continue to maintain and improve the production-ready toolset. We have already begun to merge browser support into the main repository. We plan to provide *one* Flutter toolkit, with one framework that will power mobile, web, and other platforms. + +Our planned work includes: + +* Support for text features such as selection and copy-paste. + +* Support for plugins. For features like location, camera, and file access, we hope to bridge mobile and the web with a single API. + +* Out-of-the-box support for technologies like Progressive Web Apps. + +* Unifying web development tooling under the existing Flutter CLI and IDE integration. + +* Debugging web applications using DevTools. + +* Improved performance, browser support, and accessibility. + +Go to [flutter.dev/web](https://flutter.dev/web) to find examples, documentation, and links to more resources. We’re excited to see what you build! \ No newline at end of file diff --git a/sites/www/content/blog/build-reactive-mobile-apps-in-flutter-companion-article/images/0k6VwvGyERET4BiU0.webp b/sites/www/content/blog/build-reactive-mobile-apps-in-flutter-companion-article/images/0k6VwvGyERET4BiU0.webp new file mode 100644 index 0000000000..aff4c21718 Binary files /dev/null and b/sites/www/content/blog/build-reactive-mobile-apps-in-flutter-companion-article/images/0k6VwvGyERET4BiU0.webp differ diff --git a/sites/www/content/blog/build-reactive-mobile-apps-in-flutter-companion-article/images/1DnixY5zJXnLe_x_eqSKkUA.webp b/sites/www/content/blog/build-reactive-mobile-apps-in-flutter-companion-article/images/1DnixY5zJXnLe_x_eqSKkUA.webp new file mode 100644 index 0000000000..aa796a3991 Binary files /dev/null and b/sites/www/content/blog/build-reactive-mobile-apps-in-flutter-companion-article/images/1DnixY5zJXnLe_x_eqSKkUA.webp differ diff --git a/sites/www/content/blog/build-reactive-mobile-apps-in-flutter-companion-article/index.md b/sites/www/content/blog/build-reactive-mobile-apps-in-flutter-companion-article/index.md new file mode 100644 index 0000000000..16dec32358 --- /dev/null +++ b/sites/www/content/blog/build-reactive-mobile-apps-in-flutter-companion-article/index.md @@ -0,0 +1,182 @@ +--- +title: "Build reactive mobile apps in Flutter — companion article" +description: "UPDATE (March 2019) — If you’re just starting to learn about Flutter and state management, I recommend you read the state management…" +publishDate: 2018-06-01 +author: filiph +image: images/1DnixY5zJXnLe_x_eqSKkUA.webp +category: tutorial +layout: blog +--- + +**UPDATE (March 2019) —** If you’re just starting to learn about Flutter and state management, I recommend you read the [state management section at flutter.dev](https://flutter.dev/docs/development/data-and-backend/state-mgmt/intro) instead of this article. I wrote both this article and the section at flutter.dev, and I think the latter is more approachable and exhaustive. It is also newer. + +At Google I/O, Matt and I gave a talk about state management called [Build reactive mobile apps in Flutter](https://www.youtube.com/watch?v=RS36gBEp8OI). If you missed it, you can watch it below: + +<YoutubeEmbed id="RS36gBEp8OI" title="Build reactive mobile apps with Flutter (Google I/O '18)" fullwidth="true"/> + + +The code for the talk is on github: + +<GitHubEmbed repo="filiph/state_experiments" title="state_experiments - Companion repository to the "Build reactive mobile apps in Flutter" talk" image="images/0k6VwvGyERET4BiU0.webp" /> + + +This article is about the things that didn’t fit into the 33-minute session. + +## ValueNotifier + +`ValueNotifier` is a basic implementation of observable that comes with the Flutter framework. It has a value, listeners (callbacks), and it calls the listeners any time the value changes. The functionality is implemented in about [200](https://github.com/flutter/flutter/blob/b9a7f1b915349210c7a80b1ac158ed43fd88f612/packages/flutter/lib/src/foundation/change_notifier.dart#L203) lines of Dart code, the vast majority of which are documentation comments. + +You can use `ValueNotifier` like this: + +``` +final counter = ValueNotifier(0); + +counter.addListener(_myCallback); + +counter.value = 10; // Calls _myCallback. +counter.value += 1; // Also calls _myCallback. + +counter.removeListener(_myCallback); + +counter.value += 1; // Doesn't call anything. + +counter.dispose(); +``` + + +In other words, whenever you set the `value` to something new, all the registered listeners are called. + +You can extend `ValueNotifier` to get more functionality. This makes the most sense with mutable values. + +``` +class CartObservable extends ValueNotifier<Cart> { + CartObservable(Cart value) : super(value); + + void add(Product product) { + value.add(product); + notifyListeners(); + } +} +``` + + +The advantage of `ValueNotifier` is that it’s simple, easy to understand, and included with Flutter. It’s also completely synchronous, which might be an advantage in some cases. In itself, it won’t help you with access (you’ll have to pass around the object somehow — through `InheritedWidget` or, for shallow widget trees, through constructors), and you’ll still have to manually call `setState()` and manage listeners. + +## Firebase / Cloud Firestore references + +In the talk, we show how to use the `StreamBuilder` widget to automatically rebuild UI on state change. + +[Cloud Firestore](https://firebase.google.com/docs/firestore/) (and its older sibling, [Firebase Realtime Database](https://firebase.google.com/docs/database/)) have great Flutter plugins ([`cloud_firestore`](https://pub.dartlang.org/packages/cloud_firestore) and [`firebase_database`](https://pub.dartlang.org/packages/firebase_database)), and these plugins use Streams. So, for example, you can just plug your Firestore snapshots into a StreamBuilder, and you’re done. + +``` +StreamBuilder( + stream: Firestore.instance.collection('baby').snapshots(), + builder: (context, snapshot) { + if (!snapshot.hasData) return const Text('Loading...'); + return ListView.builder( + itemCount: snapshot.data.documents.length, + itemBuilder: (context, index) { + DocumentSnapshot ds = snapshot.data.documents[index]; + return Text("${ds['name']} ${ds['votes']}"); + } + ); + }, +); +``` + + +This is really all you need if your app only presents data, or if its business logic is very straightforward. You can learn more in the [Firebase for Flutter codelab](https://codelabs.developers.google.com/codelabs/flutter-firebase/index.html#0). + +Of course, once your app grows, you can move away from exposing the raw Firebase stream to the widgets, and wrap it instead. For example, a business logic component can internally communicate with a real time database but the widgets don’t need to know about it. + +## InheritedWidget + +We used `InheritedWidget` extensively in our talk: implicitly with `ScopedModel`, which builds on `InheritedWidget`, and explicitly with BLoC, to pass around references to data streams. `InheritedWidget` is part of the core Flutter platform, and provides an easy means to make data accessible anywhere in a widget tree. + +Defining an `InheritedWidget` is simple: + +``` +class MyInheritedValue extends InheritedWidget { + const MyInheritedValue({ + Key key, + @required this.value, + @required Widget child, + }) : assert(value != null), + assert(child != null), + super(key: key, child: child); + + final int value; + + static MyInheritedValue of(BuildContext context) { + return context.inheritFromWidgetOfExactType(MyInheritedValue); + } + + @override + bool updateShouldNotify(MyInheritedValue old) => + value != old.value; +} +``` + + +It’s also easy to provide it to an ancestor: + +``` +Widget build(BuildContext context) { + return MyInheritedValue( + value: 42, + child: ... + ); +} +``` + + +And it’s easy to use it down the tree. + +``` +MyInheritedValue.of(context).value +``` + + +While `InheritedWidget` is useful for keeping your widget tree DRY and encapsulated (since references to data do not need to be passed explicitly), note that the data is immutable. In order to use `InheritedWidget` to track changing data, either a) wrap it in a `StatefulWidget` or b) use streams or `ValueNotifier` inside the `InheritedWidget`. + +## Redux + +What more can be said about Redux that hasn’t already been said elsewhere? Suffice to say that Dart has an [implementation](https://github.com/brianegan/flutter_architecture_samples/tree/master/example/redux) of Redux, and there’s a Flutter [extension](https://pub.dartlang.org/packages/flutter_redux) that provides an elegant Widget framework on top. + +In practice, the widget structure looks very similar to Scoped Model, where a `StoreProvider` gives access to state throughout the widget tree, via the `StoreConnector`. + +There’s a simple example of using Redux to manage state in Flutter’s default incrementer app [on the pub site](https://pub.dartlang.org/packages/flutter_redux). There’s also a more sophisticated example with a [todo sample app](https://github.com/brianegan/flutter_architecture_samples/tree/master/example/redux). If you’re interested in using Redux in Flutter, check these out. + +## Business Logic Components (BLoC) + +If you’re interested in the concept of business logic components, check out Paolo Soares’ talk from DartConf (January 2018): + +<YoutubeEmbed id="PLHln7wHgPE" title="Flutter / AngularDart - Code sharing, better together (DartConf 2018)" fullwidth="true"/> + + +The BLoC pattern was conceived by Cong Hui, Paolo Soares, and Wenfan Huang at Google. As you can see in Paolo’s talk, there is a lot more to BLoC than what we discussed in our I/O session. Among other things, BLoC allows Google to share code between Flutter (mobile) and AngularDart (web) apps, and Paolo shares some guidance about that. Here, I’d like to focus on the things that apply to Flutter regardless of whether you’re also using the component elsewhere. + +With that, some additional notes about this pattern: + +* BLoC doesn’t assume a particular way to get access to the component. In the talk, I’m showing how you might do that with `InheritedWidget` but nothing stops you from passing it down manually through constructors, or using some form of automatic dependency injection. + +* You should avoid having one BLoC as a parameter of another BLoC. Instead, plug only the required outputs to the applicable inputs. This helps avoid [tight coupling](https://stackoverflow.com/questions/2832017/what-is-the-difference-between-loose-coupling-and-tight-coupling-in-the-object-o). + +* Large apps need more than one BLoC. A good pattern is to use one top-level component for each screen, and one for each complex-enough widget. Too many BLoCs can become cumbersome, though. Also, if you have hundreds upon hundreds of observables (streams) in your app, that has a negative impact on performance. In other words: don’t over-engineer your app. + +* In a hierarchy of BLoCs, the top-level (screen) BLoC is normally responsible for plugging streams of children BLoCs to each other. More on that in a later article. + +* BLoC is compatible with server logic. The pattern doesn’t force you to re-implement that logic on the client (like Flux/Redux would). Just wrap the server-side logic with a component. + +* One disadvantage that stems from the asynchronicity of streams is that when you build a `StreamBuilder`, it always shows `initialData` first (because it is available synchronously) and only *then* does it show the first event coming from the `Stream`. This can lead to a flash (one frame) of missing data. There are ways to prevent this — stay tuned for a more detailed article. **UPDATE**: If using `rxdart` version `0.19.0` and above, [you can just use `ValueObservable`](https://twitter.com/filiphracek/status/1050798900968181761) for outputs and the flash of async is no longer an issue. + +* The inside of the BLoC is often implemented in a purely functional-reactive way (no auxiliary state, pure transformations of one stream to another). But don’t feel obligated to do it this way. Sometimes, it’s easier and more readable/maintainable to express the business logic through hybrid imperative-functional approach (like I was doing in the I/O talk, although that was mainly to save time). + +Some people asked for a more complex BLoC sample. I recreated the shopping app into a more realistic example where the catalog of products is fetched from the network page by page, and we have an infinite list of these products. Also, for each product in the catalog, we want to change the presentation of the ProductSquare slightly when the product is already in the catalog. The network calls are simulated but the complexity of wiring different things together is there. + +<DashImage figure src="images/1DnixY5zJXnLe_x_eqSKkUA.webp" alt="You can see how, in this version of the sample, the products are loaded in batches, and the `ProductSquare`s show whether the product is in the cart (by underlining the product name, for simplicity)." caption="You can see how, in this version of the sample, the products are loaded in batches, and the `ProductSquare`s show whether the product is in the cart (by underlining the product name, for simplicity)." /> + + +You can find the code in the companion repository, at [`lib/src/bloc_complex`](https://github.com/filiph/state_experiments/tree/master/shared/lib/src/bloc_complex). There’s a README file with more information about this version. + +We hope the talk and this article have been useful. You might want to watch [other Flutter talks and videos on YouTube](https://www.youtube.com/playlist?list=PLOU2XLYxmsIJ7dsVN4iRuA7BT8XHzGtCr). \ No newline at end of file diff --git a/sites/www/content/blog/building-a-qr-code-widget-in-flutter/images/0_19ljt157eUKJMX7.jpg b/sites/www/content/blog/building-a-qr-code-widget-in-flutter/images/0_19ljt157eUKJMX7.jpg new file mode 100644 index 0000000000..9fa582c6c3 Binary files /dev/null and b/sites/www/content/blog/building-a-qr-code-widget-in-flutter/images/0_19ljt157eUKJMX7.jpg differ diff --git a/sites/www/content/blog/building-a-qr-code-widget-in-flutter/images/1iuOxHUF0bBlZtiktVx6XyA.webp b/sites/www/content/blog/building-a-qr-code-widget-in-flutter/images/1iuOxHUF0bBlZtiktVx6XyA.webp new file mode 100644 index 0000000000..93e4c25061 Binary files /dev/null and b/sites/www/content/blog/building-a-qr-code-widget-in-flutter/images/1iuOxHUF0bBlZtiktVx6XyA.webp differ diff --git a/sites/www/content/blog/building-a-qr-code-widget-in-flutter/index.md b/sites/www/content/blog/building-a-qr-code-widget-in-flutter/index.md new file mode 100644 index 0000000000..1f55653b18 --- /dev/null +++ b/sites/www/content/blog/building-a-qr-code-widget-in-flutter/index.md @@ -0,0 +1,73 @@ +--- +title: "Building a QR code Widget in Flutter" +description: "Making Flutter Widgets is fun so get going!" +publishDate: 2018-03-05 +author: lukeaf +image: images/1iuOxHUF0bBlZtiktVx6XyA.webp +category: tutorial +layout: blog +--- + +In my latest (client) project I needed a way to get a QR code into a Flutter app. Turns out it [used to be super easy](https://developers.google.com/chart/infographics/docs/qr_codes) with the [Google Chart API](https://developers.google.com/chart/). Unfortunately, that API is deprecated now so it’s probably best to avoid it. *Google, if you’re reading, it’s probably a good addition to the new Charts API.* + +So with the Google API option out the window, it was time to look for a different solution. Enter [qr.dart](https://pub.dartlang.org/packages/qr) written by the world renowned conference host and Dart developer, [Kevin Moore](https://github.com/kevmoo/). qr.dart makes the whole generation thing a cinch so we only have to focus on the “getting the image onto the screen” bit. But first, let’s talk about how we want to define all this. If you don’t care or just want to see the code, skip the next section. + +## Defining our Widget + +The way that qr.dart (and QR codes) works is that you give it a QR code **version** and **error correction level**. The Higher the [version](http://www.qrcode.com/en/about/version.html), the more data you can store. The error correction value determines how much of the QR code can be missing / damaged before it is unusable (up to 30%). However, a higher error correction value increases the data needed for redundancy which reduces your total allowed characters so keep that in mind. Anyway, that’s the low down on QR codes for our purposes. + +Rather than just jumping into code, let’s talk about how we want to define the Widget so that it’s helpful to everyone and not just something we shove into our code. I feel like most tutorials of this kind don’t bother, so I figured let’s give this a try. Anyhow, let’s move to list form: + +1. The error correction is probably not super important (in this case) because we’re only going to draw to a screen and it’s unlikely that we will lose 30% of it so probably **Low** is a good default. The developer can always change these. + +1. Most people probably only need a small amount of information so we should set a default value so that not everyone has to go off and find out the data limitations of version 26 of the QR code standard. So, let’s pick **Version 4** as our default, which gives us 114 alphanumeric chars at Low error correction. + +1. We want to be able to determine the size of the QR code image either explicitly or implicitly; the squares should be proportional to that. You probably should pick a size that best allows for a QR code reader to read it effectively but, generally, most scanners are pretty good. + +1. Seeing as we’re doing custom painting we probably can do a painter and a Widget for maximum flexibility. + +1. We probably want to customize colours and stuff. + +We could keep going but I figure this is a good start so let’s go write some code. + +## The code + +Let’s start with the painter. Here is the full source code: +[Gist error: HTTP 404 Not Found: {"message":"Not Found","documentation_url":"https://docs.github.com/rest/gists/gists#get-a-gist","status":"404"}] + +The built **QrCode** will return a **moduleCount** that determines the square size (W x H) of the QR code so that you can iterate over each grid square to determine if it **isDark()** or is not. The rest of the code is setup, error checking, and checks to see if the data has changed. As this is the painter and not the widget, all the properties are **required**. + +OK, so on to the **Widget**. Here is the code: +[Gist error: HTTP 404 Not Found: {"message":"Not Found","documentation_url":"https://docs.github.com/rest/gists/gists#get-a-gist","status":"404"}] + +There is basically nothing in this Widget. Its mostly setup because the painter and qr.dart do all the heavy lifting. Let’s focus on the **build()** method because that’s the most important. + +The painter itself is contained in a [**CustomPaint**](https://docs.flutter.io/flutter/widgets/CustomPaint-class.html) Widget and that will do all the necessary bits and pieces to (re)draw our final QR code so we don’t have to worry about anything. However, we do want to constrain our **CustomPaint** somehow so that the painter knows an appropriate **size** value so it can do its magic. That’s where container comes in. + +As you can see on *line 20 + 21*, this is where the developer can constrain the **Container** size if they want. If no size is set then the **Container** will be inherently zero sized unless it is inside something else that determines its size. This is great because it allows for a lot of flexibility if you want to explicitly define the size in a different part of your app without having to hard code it or define it explicitly on your **QrImage** instance. + +That’s pretty much all she wrote on the subject, so, how does it all look? With a simple **QrImage** definition ****like so: + +``` +new QrImage( + data: "Hello, world in QR form!", + size: 200.0, +), +``` + + +You can create the following QR code in your app: + +<DashImage figure src="images/1iuOxHUF0bBlZtiktVx6XyA.webp" alt="Beaut!" caption="Beaut!" /> + + +All this will be in pub soon so you wont’ have to reproduce it for your purposes, but if you want to then have at it because it is open source. Hopefully, beyond getting a cool QR code Widget for your app, this gave you some small insight into how quick and easy it is to build a Widget in Flutter. + +Writing Widgets in Flutter is super easy and super fast. Flutter is ramping up and it needs **high quality** Widgets so taking the time to plan things out is super important. But, my advice, is to share what you have anyway so that people can build on it or help you out. + +The finished product is available on [Github](https://github.com/lukef/qr.flutter) and on [pub](https://pub.dartlang.org/packages/qr_flutter). + +<GitHubEmbed repo="lukef/qr.flutter" title="qr.flutter - Create QR codes in Flutter" image="images/0_19ljt157eUKJMX7.jpg" /> + + +And that’s it. If you have specific questions feel free to reach out on the Flutter [Gitter](https://gitter.im/flutter/flutter) or leave a comment here and I’ll get back to you! \ No newline at end of file diff --git a/sites/www/content/blog/building-beautiful-flexible-user-interfaces-with-flutter-material-theming-and-official-material/images/13U83sHXcjpSZCceOlIjyHg.webp b/sites/www/content/blog/building-beautiful-flexible-user-interfaces-with-flutter-material-theming-and-official-material/images/13U83sHXcjpSZCceOlIjyHg.webp new file mode 100644 index 0000000000..36fc59c6d4 Binary files /dev/null and b/sites/www/content/blog/building-beautiful-flexible-user-interfaces-with-flutter-material-theming-and-official-material/images/13U83sHXcjpSZCceOlIjyHg.webp differ diff --git a/sites/www/content/blog/building-beautiful-flexible-user-interfaces-with-flutter-material-theming-and-official-material/images/1L2vOm-w6u4c-WRU6qZ9W_A.webp b/sites/www/content/blog/building-beautiful-flexible-user-interfaces-with-flutter-material-theming-and-official-material/images/1L2vOm-w6u4c-WRU6qZ9W_A.webp new file mode 100644 index 0000000000..02ba1826a2 Binary files /dev/null and b/sites/www/content/blog/building-beautiful-flexible-user-interfaces-with-flutter-material-theming-and-official-material/images/1L2vOm-w6u4c-WRU6qZ9W_A.webp differ diff --git a/sites/www/content/blog/building-beautiful-flexible-user-interfaces-with-flutter-material-theming-and-official-material/images/1cyTGpzWuHqvYFGTV7uQyXA.webp b/sites/www/content/blog/building-beautiful-flexible-user-interfaces-with-flutter-material-theming-and-official-material/images/1cyTGpzWuHqvYFGTV7uQyXA.webp new file mode 100644 index 0000000000..be6ce4748c Binary files /dev/null and b/sites/www/content/blog/building-beautiful-flexible-user-interfaces-with-flutter-material-theming-and-official-material/images/1cyTGpzWuHqvYFGTV7uQyXA.webp differ diff --git a/sites/www/content/blog/building-beautiful-flexible-user-interfaces-with-flutter-material-theming-and-official-material/index.md b/sites/www/content/blog/building-beautiful-flexible-user-interfaces-with-flutter-material-theming-and-official-material/index.md new file mode 100644 index 0000000000..2d61774cd1 --- /dev/null +++ b/sites/www/content/blog/building-beautiful-flexible-user-interfaces-with-flutter-material-theming-and-official-material/index.md @@ -0,0 +1,61 @@ +--- +title: "Building beautiful, flexible user interfaces with Flutter, Material Theming, and official Material…" +description: "At Google I/O 2018, the Material team announced a important update to Material Design, with a strong focus on enabling mobile apps to stand…" +publishDate: 2018-05-10 +author: mit-mit +image: images/1cyTGpzWuHqvYFGTV7uQyXA.webp +category: announcements +layout: blog +--- + +## Building beautiful, flexible user interfaces with Flutter, Material Theming, and official Material Components (MDC) + +At [Google I/O 2018](https://events.google.com/io/), the Material team [announced](https://design.google/library/making-more-with-material/) a important update to Material Design, with a strong focus on enabling mobile apps to stand out from the rest by systematically applying brand-specific design. This is [Material Theming](https://material.io/design/material-theming/). The following study shows customized Material components, combined together to create a brand-specific design for ‘Shrine’, an e-commerce app that sells clothing and home goods. [Flutter](http://flutter.io) is the perfect framework for realizing designs like this! + +*“I’m incredibly excited to welcome Flutter into the official set of Material Design Components as a full fledged peer to our Android, iOS and Web offerings. Flutter’s philosophy of flexible and adaptable widgets is a great fit for Material Theming, and Flutter’s ability for real time UI iteration is a game changer in the way we polish and refine designs.”* + +- Matías Duarte, VP, Material Design + +<DashImage figure src="images/1cyTGpzWuHqvYFGTV7uQyXA.webp" alt="UI elements from the ‘Shrine brand-specific design’ using +Material Theming and Material Components" caption="UI elements from the ‘Shrine brand-specific design’ using +Material Theming and Material Components" /> + + +<DashImage figure src="images/1L2vOm-w6u4c-WRU6qZ9W_A.webp" alt="A screenshot from the Shrine design implemented using Flutter & Material Components" caption="A screenshot from the Shrine design implemented using Flutter & Material Components" /> + + +### Official Flutter support for Material Components + +One of Flutter’s core tenets is best-in-class support for creating expressive and flexible mobile UI. To support this goal, we are delighted to announce that Material has now adopted Flutter as a first-class platform! Flutter is included in design and engineering discussions, documentation, official support, content from Google Design, and education along with Android, iOS, and web. Material has even started a dedicated Flutter engineering team that works hand-in-hand with Flutter’s Material library engineers. This partnership will allow Flutter to stay even more current as Material Design continues to evolve and add features like Material Theming. You can find more information at [material.io/develop/flutter](http://material.io/develop/flutter). + +### Using Material Theming and Material Components in Flutter + +Just in time for I/O, Flutter’s Material Components library has been updated to support dozens of new features, styles, and components from the updated Material system. These are available in [Flutter beta 3](https://medium.com/flutter-io/flutter-beta-3-7d88125245dc), and are built right into the Flutter framework with no additional libraries needed! We have also worked on adding more examples of how to use these widgets in the [Flutter Gallery](https://play.google.com/store/apps/details?id=io.flutter.demo.gallery): + +<DashImage figure src="images/13U83sHXcjpSZCceOlIjyHg.webp" alt="A selection of Material Components from the Flutter Gallery" caption="A selection of Material Components from the Flutter Gallery" /> + + +## Learning more + +For a quick introduction to Material Theming with Flutter, check-out our Google I/O session: + +<YoutubeEmbed id="hA0hrpR-o8U" title="Code beautiful UI with Flutter and Material Design (Google I/O '18)" fullwidth="true"/> + + +Lastly, we are happy to announce four new codelabs that teach core concepts required to create beautiful and flexible user interfaces with Flutter and Material Components: + +1. [**MDC 101 Flutter: Material Components Basics** +](https://codelabs.developers.google.com/codelabs/mdc-101-flutter/)Learn the basics of using Material Components by building a simple app with core components. + +1. [**MDC 102 Flutter: Material Structure and Layout** +](https://codelabs.developers.google.com/codelabs/mdc-102-flutter/)Learn how to use Material for structure and layout in Flutter, adding navigation, structure, and data. + +1. [**MDC 103 Flutter: Material Theming with Color, Shape, Elevation, and Type** +](https://codelabs.developers.google.com/codelabs/mdc-103-flutter/)Use Material Components for Flutter to differentiate your product, and express your brand through design. + +1. [**MDC 104 Flutter: Material Advanced Components** +](https://codelabs.developers.google.com/codelabs/mdc-104-flutter/)Improve your design and learn to use our advanced component backdrop menu. + +### Next steps + +Flutter will continue to release features in partnership with Material. Learn more about the Material Components release schedule in the [Material Components Roadmap on GitHub](https://github.com/material-components/material-components/blob/develop/ROADMAP.md). We look forward to seeing what creative designs you all create using Flutter and Material Theming! \ No newline at end of file diff --git a/sites/www/content/blog/building-performant-flutter-widgets/images/0Bm6-mK_lPlO1deUS.webp b/sites/www/content/blog/building-performant-flutter-widgets/images/0Bm6-mK_lPlO1deUS.webp new file mode 100644 index 0000000000..417f757c53 Binary files /dev/null and b/sites/www/content/blog/building-performant-flutter-widgets/images/0Bm6-mK_lPlO1deUS.webp differ diff --git a/sites/www/content/blog/building-performant-flutter-widgets/images/0GWFHSeCrjUgPdkJ-.webp b/sites/www/content/blog/building-performant-flutter-widgets/images/0GWFHSeCrjUgPdkJ-.webp new file mode 100644 index 0000000000..393309c0f2 Binary files /dev/null and b/sites/www/content/blog/building-performant-flutter-widgets/images/0GWFHSeCrjUgPdkJ-.webp differ diff --git a/sites/www/content/blog/building-performant-flutter-widgets/images/0Q4WC3xmG1iRG1-lp.webp b/sites/www/content/blog/building-performant-flutter-widgets/images/0Q4WC3xmG1iRG1-lp.webp new file mode 100644 index 0000000000..72c2cf3372 Binary files /dev/null and b/sites/www/content/blog/building-performant-flutter-widgets/images/0Q4WC3xmG1iRG1-lp.webp differ diff --git a/sites/www/content/blog/building-performant-flutter-widgets/images/0RtYWMsCJ83boCm6h.webp b/sites/www/content/blog/building-performant-flutter-widgets/images/0RtYWMsCJ83boCm6h.webp new file mode 100644 index 0000000000..35662f2517 Binary files /dev/null and b/sites/www/content/blog/building-performant-flutter-widgets/images/0RtYWMsCJ83boCm6h.webp differ diff --git a/sites/www/content/blog/building-performant-flutter-widgets/images/0Zl3QkQlHJMoDkDQj.webp b/sites/www/content/blog/building-performant-flutter-widgets/images/0Zl3QkQlHJMoDkDQj.webp new file mode 100644 index 0000000000..417f757c53 Binary files /dev/null and b/sites/www/content/blog/building-performant-flutter-widgets/images/0Zl3QkQlHJMoDkDQj.webp differ diff --git a/sites/www/content/blog/building-performant-flutter-widgets/index.md b/sites/www/content/blog/building-performant-flutter-widgets/index.md new file mode 100644 index 0000000000..b15ba48db0 --- /dev/null +++ b/sites/www/content/blog/building-performant-flutter-widgets/index.md @@ -0,0 +1,304 @@ +--- +title: "Building performant Flutter widgets" +description: "This article is part of a series developed after the Flutter Material team worked on making the Flutter Gallery app more performant on the…" +publishDate: 2020-07-23 +author: guidez +image: images/0GWFHSeCrjUgPdkJ-.webp +category: tutorial +layout: blog +--- + +*This article is part of a series developed after the Flutter Material team worked on making the Flutter Gallery app more performant on the web. However, the tips in this article apply to **all** Flutter applications. Skip to the end to find the other articles in this series.* + +By [Anthony Robledo](https://medium.com/@clocksmith) & [Pierre-Louis Guidez](https://medium.com/@guidez) + +<DashImage figure src="images/0GWFHSeCrjUgPdkJ-.webp" /> + + +All stateless and stateful widgets implement `build()` methods that define how they’re rendered. A screen on an app can have hundreds or even thousands of widgets. These widgets may get built only once, or multiple times if there is an animation or some kind of interaction. While building widgets is relatively fast in Flutter, you must be vigilant in when and what you choose to build. + +This article talks about building only what you need and only when you need it. Then we share how we used this approach to achieve a significant performance improvement in the Flutter Gallery web app. We’ll also share pro tips on how you can diagnose similar problems in your web app. + +## Only build when necessary + +One important optimization is to build widgets only when it’s absolutely necessary. + +### Call `setState()` judiciously + +Calling `setState` schedules a `build()` method to be called. Doing this too often can slow down the performance of a screen. + +Consider the following animation, where the display of the front (the black screen) is animated to slide down to reveal the back (the checkerboard), similar to how a [bottom sheet](https://material.io/components/sheets-bottom) might behave. The front widget is simple, but the back widget is busy: + +<DashImage figure src="images/0Bm6-mK_lPlO1deUS.webp" alt="A smooth animation" caption="A smooth animation" /> + + +``` +Stack( + children: [ + Back(), + PositionedTransition( + rect: RelativeRectTween( + begin: RelativeRect.fromLTRB(0, 0, 0, 0), + end: RelativeRect.fromLTRB(0, MediaQuery.of(context).size.height, 0, 0), + ).animate(_animationController), + child: Front(), + ) + ], + ), +``` + + +You might be tempted to set up the parent widget as follows, but in this scenario, this is wrong! + +``` +// BAD CODE +[@override](http://twitter.com/override) +void initState() { + super.initState(); + _animationController = AnimationController( + duration: Duration(seconds: 3), + vsync: this, + ); + _animationController.addListener(() { + setState(() { + // Rebuild when animation ticks. + }); + }); +} +``` + + +This is not performant! Why not? + +Because the animation is doing unnecessary work. + +<DashImage figure src="images/0Q4WC3xmG1iRG1-lp.webp" alt="A janky animation" caption="A janky animation" /> + + +Here is the problematic code: + +``` +// BAD CODE +_animationController.addListener(() { + setState(() { + // Rebuild when animation ticks. + }); +}); +``` + + +* This style of animation is recommended when you need to animate the entire widget, but that is not what we are doing here. + +* Calling `setState()` in the animation listener causes the entire `Stack` to be rebuilt, which is not necessary! + +* The `PositionedTransition` widget is already an `AnimatedWidget`, so it rebuilds *automatically* when the animation ticks. + +* Calling `setState()` is actually not needed here! + +<DashImage figure src="images/0Zl3QkQlHJMoDkDQj.webp" /> + + +Even though the back widget is busy, it can animate smoothly at 60 FPS. For more on calling setState judiciously, see [Flutter Laggy Animations: How Not To setState](https://medium.com/flutter-community/flutter-laggy-animations-how-not-to-setstate-f2dd9873b8fc). + +## Only build *what* is necessary + +In addition to only building when it’s required, you want to build only the portion of the UI that actually changes. The following section focuses on creating performant lists. + +### Prefer ListView.builder() + +First, let’s briefly cover the basics of displaying lists. + +1. To layout list items vertically, use a `Column`. + +1. If the list should be scrollable, use a `ListView` instead. + +1. If the list contains many items, use the `ListView.builder` constructor, which creates items as they scroll onto the screen instead of all at once. This has obvious performance benefits for complex list items and deep widget subtrees. + +To illustrate the benefits of `ListView.builder` over `ListView` when you have a large number of list items, let’s look at a couple of examples. + +Run the following`ListView` [example in DartPad](https://dartpad.dev/e41ed2678b9b9d7347880c20ec49f3f2). Observe that all 8 items are created. (Click **Console** in the lower left to display the console, and then click **Run**. The output window has no scrollbar, but you can scroll the content and observe the console to see what is created and built when.) + +``` +ListView( + children: [ + _ListItem(index: 0), + _ListItem(index: 1), + _ListItem(index: 2), + _ListItem(index: 3), + _ListItem(index: 4), + _ListItem(index: 5), + _ListItem(index: 6), + _ListItem(index: 7), + ], +); +``` + + +Next, run the `ListView.builder` [example in DartPad](https://dartpad.dev/1ae687f1c0d17eb80c8e28a70fb5b8d1). Observe that only the visible items are created. As you scroll, it creates (and builds) new rows. + +``` +ListView.builder( + itemBuilder: (context, index) { + return _ListItem(index: index); + }, + itemCount: 8, +); +``` + + +Now, run the [example in DartPad](https://dartpad.dev/a338a69afea04f746015861cd55782db) where`ListView`’s children are created in advance, all at once when the `ListView` itself is created. In this scenario, it’s more efficient to use the `ListView` constructor. + +``` +final listItems = [ + _ListItem(index: 0), + _ListItem(index: 1), + _ListItem(index: 2), + _ListItem(index: 3), + _ListItem(index: 4), + _ListItem(index: 5), + _ListItem(index: 6), + _ListItem(index: 7), +]; + +@override +Widget build(BuildContext context) { + // This offers no benefit, it is actually more efficient to use the ListView constructor instead. + return ListView.builder( + itemBuilder: (context, index) { + return listItems[index]; + }, + itemCount: 8, + ); +} +``` + + +For more on lazily building lists, see [Slivers, Demystified](https://medium.com/flutter/slivers-demystified-6ff68ab0296f). + +## How we improved the Flutter Gallery web page render time by more than 2x with a single line of code + +The Flutter Gallery supports over 100 locales; Those locales are listed using — you guessed it — a `ListView.builder`. By obtaining widget rebuild information, we noticed these list items were being built unnecessarily on startup. It was not obvious that these items were the culprit since they were in two levels of collapsed menus: the settings panel itself, and the locale expansion tile (as it turns out, the settings panel was rendered ‘invisible’ using a `ScaleTransition`, meaning it was very much being built). + +<DashImage figure src="images/0RtYWMsCJ83boCm6h.webp" alt="Flutter Gallery settings panel with the locale options expanded" caption="Flutter Gallery settings panel with the locale options expanded" /> + + +By simply setting `ListView.builder`’s `itemCount` to 0 for non-expanded setting categories, we ensured that list items are only built for the expanded, visible category. The [one-line PR](https://github.com/flutter/gallery/pull/109/files) that resolved this issue improved render time on the web by more than 2x. The key was to identify excessive widget building. + +## Seeing widget build counts for your application + +While Flutter builds very efficiently, there are some cases where building excessively can cause performance issues. There are a few ways to identify excessive widget rebuilding. + +### By using Android Studio/IntelliJ + +Android Studio and IntelliJ developers can use built-in tooling to [show Widget rebuild information](https://flutter.dev/docs/development/tools/android-studio#show-performance-data). + +### By modifying the framework + +If you use a different editor, or would like to know widget rebuild information for the web, you can do so by adding a bit of code to the framework. + +Sample output: + +``` +RaisedButton 1 +RawMaterialButton 2 +ExpensiveWidget 538 +Header 5 +``` + + +Locate `<Flutter path>/packages/flutter/lib/src/widgets/framework.dart`. Add the following code, which counts the number of times widgets are built at startup, and outputs the results after some duration (here, 10 seconds). + +``` +bool _outputScheduled = false; +Map<String, int> _outputMap = <String, int>{}; +void _output(Widget widget) { + final String typeName = widget.runtimeType.toString(); + if (_outputMap.containsKey(typeName)) { + _outputMap[typeName] = _outputMap[typeName] + 1; + } else { + _outputMap[typeName] = 1; + } + if (_outputScheduled) { + return; + } + _outputScheduled = true; + Timer(const Duration(seconds: 10), () { + _outputMap.forEach((String key, int value) { + switch (widget.runtimeType.toString()) { + // Filter out widgets whose build counts we don't care about + case 'InkWell': + case 'RawGestureDetector': + case 'FocusScope': + break; + default: + print('$key $value'); + } + }); + }); +} +``` + + +Then, modify the `build` methods for `StatelessElement` and `StatefulElement` to call `_output(widget)`. + +``` +class StatelessElement extends ComponentElement { + ... + +@override + Widget build() { + final Widget w = widget.build(this); + _output(w); + return w; + } + +class StatefulElement extends ComponentElement { +... + +@override + Widget build() { + final Widget w = _state.build(this); + _output(w); + return w; + } +``` + + +See the resulting [framework.dart](https://gist.github.com/guidezpl/54f9a03b0adbf207153178dba0bf214c) file. + +Note that numerous rebuilds doesn’t necessarily indicate a problem. However, it can help debug performance issues by verifying that non-visible widgets aren’t being built, for example. + +**Tip for web only**: you can add a `resetOutput` function (that can be called from a browser’s developer tools) to obtain widget build counts at any point in time. + +``` +import 'dart:js' as js; + +void resetOutput() { + _outputScheduled = false; + _outputMap = <String, int>{}; +} + +void _output(Widget widget) { + // Add this line + js.context['resetOutput'] = resetOutput; + ... +``` + + +See the resulting [framework.dart](https://gist.github.com/guidezpl/32518a6d22596393fa368c28e8f0ece4) file. + +## Closing Remarks + +Effective performance debugging requires understanding what’s happening under the hood. These tips can help decide how to implement your next build method so that your app stays performant in all scenarios. + +This post is a part of a series about what we learned when improving performance for the Flutter Gallery. Articles in the *Creating performant Flutter web apps* series: + +* [Optimizing performance in Flutter web apps with tree shaking and deferred loading](https://medium.com/flutter/optimizing-performance-in-flutter-web-apps-with-tree-shaking-and-deferred-loading-535fbe3cd674) + +* [Improving perceived performance with image placeholders, precaching, and disabled navigation transitions](https://medium.com/flutter/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation-6b3601087a2b) + +* Building performant Flutter widgets (this article) + +For more general information, the Flutter [docs on UI performance](https://flutter.dev/docs/perf/rendering/ui-performance) are a great place for developers of all levels to start. + +Thanks to Shams Zakhour for editing this article and Ferhat Buyukkokten for debugging tips. \ No newline at end of file diff --git a/sites/www/content/blog/building-your-next-casual-game-with-flutter/images/00GvQrqyBDG2NVb42.webp b/sites/www/content/blog/building-your-next-casual-game-with-flutter/images/00GvQrqyBDG2NVb42.webp new file mode 100644 index 0000000000..5938db7f5c Binary files /dev/null and b/sites/www/content/blog/building-your-next-casual-game-with-flutter/images/00GvQrqyBDG2NVb42.webp differ diff --git a/sites/www/content/blog/building-your-next-casual-game-with-flutter/images/03DmGJe1yD4eKdnfs.webp b/sites/www/content/blog/building-your-next-casual-game-with-flutter/images/03DmGJe1yD4eKdnfs.webp new file mode 100644 index 0000000000..23602bbd88 Binary files /dev/null and b/sites/www/content/blog/building-your-next-casual-game-with-flutter/images/03DmGJe1yD4eKdnfs.webp differ diff --git a/sites/www/content/blog/building-your-next-casual-game-with-flutter/images/0SDayAkeAnC_NtiLU.webp b/sites/www/content/blog/building-your-next-casual-game-with-flutter/images/0SDayAkeAnC_NtiLU.webp new file mode 100644 index 0000000000..ce387a7646 Binary files /dev/null and b/sites/www/content/blog/building-your-next-casual-game-with-flutter/images/0SDayAkeAnC_NtiLU.webp differ diff --git a/sites/www/content/blog/building-your-next-casual-game-with-flutter/images/0_MYAbH0pmAhJ3D6B.webp b/sites/www/content/blog/building-your-next-casual-game-with-flutter/images/0_MYAbH0pmAhJ3D6B.webp new file mode 100644 index 0000000000..d04a48a789 Binary files /dev/null and b/sites/www/content/blog/building-your-next-casual-game-with-flutter/images/0_MYAbH0pmAhJ3D6B.webp differ diff --git a/sites/www/content/blog/building-your-next-casual-game-with-flutter/index.md b/sites/www/content/blog/building-your-next-casual-game-with-flutter/index.md new file mode 100644 index 0000000000..2e51eb58b3 --- /dev/null +++ b/sites/www/content/blog/building-your-next-casual-game-with-flutter/index.md @@ -0,0 +1,69 @@ +--- +title: "Building your next casual game with Flutter" +description: "Free & open source multi-platform 2D game development in Flutter" +publishDate: 2023-11-15 +author: zoeyfan +image: images/03DmGJe1yD4eKdnfs.webp +category: announcements +layout: blog +--- + +Today I’m excited to introduce you to a major update of the [Flutter Casual Games Toolkit](http://flutter.dev/games), a collection of new templates and new developer resources to make game developers more productive with Flutter. + +Flutter is Google’s open source UI framework for building beautiful multi-platform applications. Today, Flutter is used in more than one million apps ranging from early stage startups to large companies like ByteDance, Tencent, BMW, and Google, to deliver beautiful, performant, and portable experiences across platforms, from a single codebase. + +Over the past few years, we’ve seen a growing community around casual game development. Tens of thousands of games have been published using Flutter, from simple but fun puzzles to more complex arcade games. Some of our favorites include Etermax’s [Trivia Crack](https://triviacrack.com/), Lotum’s [4 Pics 1 Word](https://flutter.dev/showcase/lotum) (word guess game), Dong Digital’s [Brick Mania](https://play.google.com/store/apps/details?id=net.countrymania.brick&hl=en) (arcade game), Onrizon’s [StopotS](https://play.google.com/store/apps/details?id=com.gartic.StopotS&hl=en) (categories game), the [retro pinball game](https://pinball.flutter.dev/) we built in Flutter for I/O, and [PUBG](https://flutter.dev/showcase/pubg-mobile) mobile who uses Flutter in their social and menu screens. + +<DashImage figure src="images/03DmGJe1yD4eKdnfs.webp" alt="An animated gif showing multiple Flutter-based games such as Trivia Crack, Debertz, and Landover running on mobile, web and desktop devices." caption="An animated gif showing multiple Flutter-based games such as Trivia Crack, Debertz, and Landover running on mobile, web and desktop devices." /> + + +## Why choose Flutter for casual games + +Flutter is a great choice for game developers. First, it’s free and open source, giving you fine-grained control over your game’s rendering and input handling logic. This allows you to debug issues at their core and customize the engine to your needs. Flutter’s openness also extends to our ecosystem. All [Flutter plugins and packages](http://pub.dev) are also available for integration at no cost. + +Second, developing in Flutter is highly productive. Flutter introduced a revolutionary capability called [hot-reload](https://docs.flutter.dev/tools/hot-reload#how-to-perform-a-hot-reload) that allows developers to see instant UI updates after making code changes, thus making the development process more iterative and efficient. Besides, Flutter supports multi-platform game development, so you can build your game for **iOS and Android, web, and desktop** all from a single, shared codebase. This saves you time and effort, and allows your game to reach a wider audience from day one. + +Finally, Flutter games load fast and are generally very performant, even on low-end devices or in browsers. Bundle sizes can be smaller because the Flutter engine only adds a few megabytes to your game. + +## The updated Flutter Casual Games Toolkit + +Since we released the first version of the toolkit in 2022, we’ve seen a lot of excitement from the Flutter community, but we have also heard strong feedback that you want more resources and guidance in developing a successful game. So we are updating the Flutter Casual Games Toolkit with new game templates, and resources to help you easily build casual games in Flutter. Just to highlight a few updates: + +### Templates for multiple game genres + +We added three new game templates to provide a starting point for building a casual game. First, you will find a [basic template](https://github.com/flutter/games/tree/main/templates/basic) that has a simple UI for the main menu, settings screen, simple level selection, and audio integration. + +<DashImage figure src="images/0_MYAbH0pmAhJ3D6B.webp" alt="An animated gif showing the basic template" caption="An animated gif showing the basic template" /> + + +Second, you will find a [Card game template](https://github.com/flutter/games/tree/main/templates/card) that builds on top of the base template and adds basic drag and drop of cards, and is ready for multiplayer integration. + +<DashImage figure src="images/00GvQrqyBDG2NVb42.webp" alt="An animated gif showing the card template" caption="An animated gif showing the card template" /> + + +Third, we partnered with the open source game engine [Flame](http://flame-engine.org) (built on top of Flutter), to provide an [endless runner template](https://github.com/flutter/games/tree/main/templates/endless_runner). While you can already use *only* Flutter to build many turn-based games such as card games, word puzzles, and board games that respond to simple user input, you might also want to build real-time games such as endless runners, shooters, and racing games that require a game loop. In those cases, you can use Flame to implement play steering, collision detection, parallax, spawning, and different visual effects. + +<DashImage figure src="images/0SDayAkeAnC_NtiLU.webp" alt="An animated gif showing the endless runner template" caption="An animated gif showing the endless runner template" /> + + +To use these templates, simply check out the [repo](https://github.com/flutter/games), and start building your game using the template that is the closest to what you want. + +### Developer resources for game service integrations + +The toolkit includes developer guides for integrating necessary services to make your game more successful. For example, we included a few new Cookbook recipes and codelabs to show you how to: + +* Build a [multiplayer game](https://docs.flutter.dev/cookbook/games/firestore-multiplayer) with Cloud Firestore. + +* Make your game more engaging with [leaderboards and achievements](https://docs.flutter.dev/cookbook/games/achievements-leaderboard). + +* Monetize your game with [in-game ads](https://docs.flutter.dev/cookbook/games/google-mobile-ads) and [in-app purchases](https://codelabs.developers.google.com/codelabs/flutter-in-app-purchases#0). + +* Add a user authentication flow with [Firebase Authentication](https://firebase.google.com/codelabs/firebase-auth-in-flutter-apps#0). + +* Collect analytics about crashes and errors with [Firebase Crashlytics](https://firebase.flutter.dev/docs/crashlytics/overview/). + +The Flutter Casual Games Toolkit, like Flutter, is available for free ([BSD 3 License](https://github.com/flutter/flutter/blob/master/LICENSE)) and is open source. You can learn more and get started at flutter.dev/games. We hope to release further updates to the toolkit with more genre-specific templates and developer resources. Please email us at [flutter-games@google.com](mailto:flutter-games@google.com) to tell us what else would make your game development experience easier! + +## Start building your games in Flutter + +We’re excited to see what you build with the Flutter Casual Games Toolkit. Whether you’re a seasoned game developer or just getting started, we have the tools and resources you need to create amazing casual games. So what are you waiting for? Start building today! \ No newline at end of file diff --git a/sites/www/content/blog/celebrating-flutters-production-era/images/03685YaIo_7soz-qF.webp b/sites/www/content/blog/celebrating-flutters-production-era/images/03685YaIo_7soz-qF.webp new file mode 100644 index 0000000000..7cf94983d3 Binary files /dev/null and b/sites/www/content/blog/celebrating-flutters-production-era/images/03685YaIo_7soz-qF.webp differ diff --git a/sites/www/content/blog/celebrating-flutters-production-era/images/0Y65O6741pXOhM35S.webp b/sites/www/content/blog/celebrating-flutters-production-era/images/0Y65O6741pXOhM35S.webp new file mode 100644 index 0000000000..d787418790 Binary files /dev/null and b/sites/www/content/blog/celebrating-flutters-production-era/images/0Y65O6741pXOhM35S.webp differ diff --git a/sites/www/content/blog/celebrating-flutters-production-era/images/1nlSy3ZCCbdUXFRH4Q1zstg.webp b/sites/www/content/blog/celebrating-flutters-production-era/images/1nlSy3ZCCbdUXFRH4Q1zstg.webp new file mode 100644 index 0000000000..21669141e6 Binary files /dev/null and b/sites/www/content/blog/celebrating-flutters-production-era/images/1nlSy3ZCCbdUXFRH4Q1zstg.webp differ diff --git a/sites/www/content/blog/celebrating-flutters-production-era/index.md b/sites/www/content/blog/celebrating-flutters-production-era/index.md new file mode 100644 index 0000000000..9e6a929231 --- /dev/null +++ b/sites/www/content/blog/celebrating-flutters-production-era/index.md @@ -0,0 +1,123 @@ +--- +title: "Celebrating Flutter’s “Production Era”" +description: "Just over six years ago, we unveiled Flutter 1.0. Today, at #FlutterInProduction, we’re celebrating how far we’ve come — from the immense…" +publishDate: 2024-12-17 +author: mit-mit +image: images/1nlSy3ZCCbdUXFRH4Q1zstg.webp +category: announcements +layout: blog +--- + +Just over six years ago, we unveiled Flutter 1.0. Today, at [**`#FlutterInProduction`**](https://flutter.dev/events/flutter-in-production), we’re celebrating how far we’ve come — from the immense support we’ve received from **thousands of contributors in the community**, to the **widespread adoption** of Flutter as a production-grade app framework for building multi-platform app experiences. + +If you haven’t experienced Flutter yet, we invite you to try it! As we shared today, you’d be joining a big group: Flutter has **over 1 million monthly active developers across the globe**, and **powers nearly 30% of all new iOS apps**. More than 90 thousand developers actively participate in Flutter Meetups across more than sixty countries. And if you want input on designing or building a new successful Flutter app, we have a large and growing list of [Flutter Consultants](https://flutter.dev/consultants) ready to help you. +> “Apptopia tracks millions of apps in the Apple AppStore and Google Play Store, and analyzes and detects which developer SDKs were used to create the apps. Flutter is one of the most popular SDKs we track: In the Apple AppStore it has grown steadily in usage from around 10% of all tracked free apps in 2021 to nearly 30% of all tracked free apps in 2024!” +> — [Apptopia Inc.](https://apptopia.com/) + +### A decade of innovation to reach the production era + +It’s been an incredible journey, starting in 2014 (in what we now call our experimental era) as a Google experiment codenamed “Sky.” + +Before Flutter, compromises were inevitable. Many developers have become skeptical that any framework can truly deliver a premium experience across multiple platforms. + +With the launch of Flutter 1.0 in 2018 we had a clear mission to resolve that technology dilemma: We aimed to empower developers with the ultimate app framework for crafting beautiful, high-performance user interfaces across all platforms. Also, to enable developers to reach all customers with high-quality apps on all the platforms that customers care about, but *with lower cost* and *in less time*. + +Our focus has remained constant through Flutter’s growth era, even as we’ve added support for the six major platforms across mobile, web, and desktop — and continue to push [beyond](https://flutter.dev/multi-platform/embedded), with work like Toyota’s use of Flutter for infotainment systems. + +<DashImage figure src="images/1nlSy3ZCCbdUXFRH4Q1zstg.webp" /> + + +We’re now in the “production era,” and we’re celebrating that with **`#FlutterInProduction`**! This event spotlights the achievements of developers using Flutter in real-world applications. + +<YoutubeEmbed id="AEXIrThTgb0" title="" fullwidth="true"/> + + +### Building in partnership with the community + +None of this would be possible without our amazing community of over 1,400 contributors, more than 10,000 package publishers authoring over 50,000 packages, and passionate Flutter content creators and enthusiasts. + +Together, we’ve built a [top-5 GitHub open-source project](https://github.blog/news-insights/octoverse/octoverse-2024/#the-state-of-open-source) by contributions! + +## Amazing user experiences + +It all starts with a focus on enabling amazing user experiences. Free from typical platform constraints, Flutter supports a broad set of design languages — support for Material Design and our Apple-inspired Cupertino widgets comes with the SDK. The ecosystem also provides a broad selection of design libraries like Windows-inspired [`fluent_ui`](https://pub.dev/packages/fluent_ui), macOS-inspired [`macos_ui`](https://pub.dev/packages/macos_ui), and the Ubuntu-inspired [`yaru`](https://pub.dev/packages/yaru) widgets. + +### Scandinavian Airlines design awards + +With Flutter, you have the flexibility and power to realize *any design* your design team envisions. This is exemplified by Scandinavian Airlines, who after creating their new mobile app with Flutter have filled their trophy case with prestigious design awards such as the Red Dot Design Award, the Webby People’s Voice Award, and the iF Design Gold Award. Charlotte Svensson, EVP & CIO at SAS [explains](https://www.sasgroup.net/newsroom/press-releases/2023/sas-wins-webby-award-for-redesigned-app/): +> “I’m extremely proud over this award, which is not just an industry award, but a global recognition. It’s a testament to what we can do, when we go above-and-beyond in focusing on improving the customer experience, and when we interact and develop together with our customers. SAS has always been at the forefront of innovation in the aviation industry, and this award serves as a validation of its dedication to providing exceptional digital solutions for our customers.” + +<DashImage figure src="images/03685YaIo_7soz-qF.webp" alt="Scandinavian Airlines design awards" caption="Scandinavian Airlines design awards" /> + + +## Great performance & reliability + +Performance and reliability are crucial for a positive user experience and brand perception. Slow or crash-prone apps not only frustrate users in the short term but can also damage your brand reputation in the long run through negative reviews and word-of-mouth. + +Flutter has prioritized performance and reliability from the outset. By choosing the Dart programming language, we ensure fast startup times through ahead-of-time compilation to native machine code or web assembly. Dart’s rich, null-safety type system helps catch errors during development, further enhancing reliability. Additionally, Flutter’s custom Impeller rendering engine, designed specifically for multi-platform UI, delivers smooth animations and gives us full control over the rendering stack, top to bottom, from the UI source code to the GPU. + +### Universal Studios performance and reliability + +For example, Universal Destinations and Experiences recently reported that by adopting Flutter, they not only decreased their app size — a significant benefit for users with unreliable internet connections — but also dramatically reduced app crashes to near zero, thus lowering their total cost of ownership. + +<YoutubeEmbed id="JTk2Exr7FO4" title="How Universal Destinations & Experiences build next generation experiences with #Flutter" fullwidth="true"/> + + +### LG Electronics performance + +LG Electronics has traditionally relied on web apps for their webOS-powered smart TVs due to concerns about the high development cost of traditional native apps. However, they found that web apps launch slower and consume more memory than native apps. With Flutter, LG Electronics has a solution that combines fast development speed and excellent performance. As a result, they plan to use Flutter for key applications in webOS TVs globally starting in 2025. + +<YoutubeEmbed id="l-J3Of7pLPw" title="How LG Electronics uses #FlutterInProduction" fullwidth="true"/> + + +## First-class developer experience and thriving ecosystem + +Flutter’s success is deeply rooted in its focus on developer experience. We pioneered instant developer workflows with Stateful Hot Reload, and during our growth era added Flutter [DevTools](https://docs.flutter.dev/tools/devtools) to significantly accelerate diagnostics and debugging workflows. + +Flutter’s community provides a thriving and open ecosystem of over 50,000 packages published by over 10,000 publishers, combined with robust third-party services & technologies. Also, if you want input on designing or building a new successful Flutter app, we have a large list of [Flutter Consultants](https://flutter.dev/consultants) ready to help you. + +### MGM and developer productivity + +App agency [Superformula](https://www.superformula.com/services/flutter/) has built with Flutter since August 2020. They found that Flutter is easy to learn and well documented, enabling them to get new team members up to speed quickly and contribute effectively. + +Superformula also used Flutter to [revitalize the digital dining experience](https://www.superformula.com/work/revitalizing-the-digital-dining-experience-for-mgm-resorts-400-restaurants/) for MGM Resorts’ 400+ restaurants. The new Flutter-based MGM Rewards app was rebuilt in just 4 months, cutting the total amount of code in half, and improving delivery speed by a factor of 4. One core enabler of productivity for Superformula is the ability to share code across mobile, tablet-based kiosks, and web-based tools. + +<YoutubeEmbed id="huzVEL676lc" title="How MGM Resorts uses #FlutterInProduction" fullwidth="true"/> + + +### GEICO and the democratization of mobile development + +Earlier this year, American auto insurance company GEICO [published a blog post](https://www.geico.com/techblog/flutter-as-the-multi-channel-ux-framework/) detailing their experiences with Flutter and how it had improved their productivity. They were able to change the structure of their organization so that UX teams now own the entire experience *across all channels*, reducing the amount of drift between the different implementations of the same functionality. They also describe Flutter’s development experience as *“mature, with rich tooling and support for the Software Development Lifecycle (SDLC)”.* + +<DashImage figure src="images/0Y65O6741pXOhM35S.webp" alt="GEICO user interface elements shared across web, iOS, and Android." caption="GEICO user interface elements shared across web, iOS, and Android." /> + + +## A sneak preview of the future + +As we said, some of the most important work the Flutter team will undertake in the next few years is to sustain the quality and depth of support demanded by the enterprises delivering Flutter-based experiences to hundreds of millions of users all over the world. + +To meet these evolving needs, we’re focusing on several key areas: + +* **Deeper iOS fidelity:** We’re continuing to deliver even greater fidelity to Apple’s design language by expanding our Cupertino widgets. And we’re embracing modern Apple ecosystem standards like Swift Package Manager. + +* **Seamless platform integration:** We’re investigating a new approach to interoperability — direct native interop — which we believe will simplify access to platform-specific APIs available in C, Java, Kotlin, ObjectiveC, or Swift. + +* **Developer productivity enhancements:** We’re continuing to evolve our developer tools by exploring potential new UI design features that may offer a live widget previewer combined with real-time visual property editing. + +* **Concise and readable code:** To boost developer productivity, we’re exploring how to make Flutter code quicker to write and easier to read. This includes potential new language features such as ‘primary constructors’ and ‘enum shorthands’, and looking through the Flutter framework to identify new APIs that may offer a more concise way of writing common code. + +As Rivers so accurately puts it, things have certainly “*gotten a little slicker, and a bit more professional*” and it’s our intention to keep things feeling that way going into the future. + +## Ready to use #FlutterInProduction? + +If you haven’t tried Flutter recently, there’s never been a better time! + +Want to learn more from others’ experiences first? + +* **Attend the [Observable Flutter panel](https://flutter.dev/events/flutter-in-production#schedule)** on December 19th, 2024 + +* **Join the Flutter community**: Find a local meetup, join the forum, or connect on Slack: [https://flutter.dev/community](https://flutter.dev/community) + +We’re dedicated to earning your trust with an unwavering focus on quality and performance. We continue to drive our roadmap from the needs of our users. That has evolved too over the last 6 years of growth, from individuals and hobbyists in our experimental era, to the large enterprises leveraging Flutter for business critical functionality today. + +As always, we can’t wait to see what you build next. Until next time! \ No newline at end of file diff --git a/sites/www/content/blog/come-meet-the-flutter-core-team-on-tour-in-2026/images/1UVUiBS4ceEJmWIt8w8IeTQ.webp b/sites/www/content/blog/come-meet-the-flutter-core-team-on-tour-in-2026/images/1UVUiBS4ceEJmWIt8w8IeTQ.webp new file mode 100644 index 0000000000..8102f37bf8 Binary files /dev/null and b/sites/www/content/blog/come-meet-the-flutter-core-team-on-tour-in-2026/images/1UVUiBS4ceEJmWIt8w8IeTQ.webp differ diff --git a/sites/www/content/blog/come-meet-the-flutter-core-team-on-tour-in-2026/index.md b/sites/www/content/blog/come-meet-the-flutter-core-team-on-tour-in-2026/index.md new file mode 100644 index 0000000000..eb81ef99ba --- /dev/null +++ b/sites/www/content/blog/come-meet-the-flutter-core-team-on-tour-in-2026/index.md @@ -0,0 +1,77 @@ +--- +title: "Come meet the Flutter core team on tour in 2026" +description: "The Flutter team has been hard at work preparing for Google Cloud Next and Google I/O this Spring. I strongly believe in the power of…" +publishDate: 2026-04-14 +author: twerske +image: images/1UVUiBS4ceEJmWIt8w8IeTQ.webp +layout: blog +--- + +<DashImage figure src="images/1UVUiBS4ceEJmWIt8w8IeTQ.webp" alt="Upcoming events where you can meet members of the Flutter team" caption="Upcoming events where you can meet members of the Flutter team" /> + + +## Come meet the Flutter core team on tour in 2026 + +The Flutter team has been hard at work preparing for Google Cloud Next and Google I/O this Spring. I strongly believe in the power of community, whether digital and online, or in-person and interactive. For Flutter, that means a truly global community and hundreds of thousands of developers around the world. Some of the most impactful moments of my career have come from those — community thrives in community-led events. + +As we kick off our year and the upcoming release of Dart 3.12 and Flutter 3.44, the Flutter team is setting out to meet as many of you as possible, around the globe. Our mission is to build the most productive app framework and language for high-performance, full-stack, multi-platform applications and ephemeral experiences. And doing that means hearing from you. + +To be transparent and open, we’ve updated [the list of events](https://flutter.dev/events) where our team will be present, or we have sponsored the presence of our Google Developer Experts. Our developer relations team is constantly engaging with developers, founders, enterprises, and builders by organizing: customer and partner advisory boards, our [meetup organizer network](https://flutter.dev/community), [Flutteristas](https://flutter.dev/community), [the Flutter consultants program](https://flutter.dev/consultants), [Google Developer Experts](https://flutter.dev/community), and [Google Developer Group organizers](https://developers.google.com/community). + +If you’ve been looking for an opportunity to connect with the team, see a live demo, or give us feedback in person, you can find us at the following events for the rest of the year: + +### April + +* [**Google Cloud Next**](https://cloud.google.com/next) (Las Vegas, NV) — April 22–24 + +### May + +* [**Flutterconf Spain**](https://flutterconf.es/) (Malaga, Spain) — May 08 + +* [**Google I/O**](https://events.google.com/io) (Sunnyvale, USA) — May 19–20 + +* [**Flutter Flow Developers Conference**](https://www.ffdc.io/) (San Francisco, CA) — May 27–28 + +### June + +* [**mDevCamp**](https://mdevcamp.eu/) (Prague, Czechia) — June 03 + +* [**Flutter Tech Summit**](https://www.fluttertechsummit.com/) (Warsaw, Poland) — June 09 + +* [**Config**](https://config.figma.com/) (San Francisco, CA) — June 23–25 + +* **I/O Connect Berlin** (Berlin, Germany) — June 25 + +### July + +* **I/O Connect Bengaluru** (Bengaluru, India) — July 14 + +* [**Fluttercon USA**](https://www.flutterconusa.dev/) (Orlando, FL) — July 16–17 + +### August + +* [**Ai4**](https://ai4.io/) (Las Vegas, NV) — August 4–6 + +* **I/O Connect China** (China) — TBD + +* [**RenderATL**](https://www.renderatl.com/) (Atlanta, GA) — August 12–13 + +### September + +* [**Flutter & Friends**](https://flutterfriends.dev/) (Stockholm, Sweden) — Sept 3–5 + +* [**FlutterConf LATAM**](https://flutterconflatam.dev/) (Cancún, México) — September 22–23 + +### October + +* [**next.app devcon**](https://www.nextappcon.com/) (Berlin, Germany) — October 7–9 + +* [**All Things Open**](https://allthingsopen.org/events/all-things-open-2026) (Raleigh, NC) — October 19–20 + +* [**Flutter Kaigi/Ninjas**](https://medium.com/flutterkaigi/flutterkaigi-2026-%E9%96%8B%E5%82%AC%E3%81%AE%E3%81%8A%E7%9F%A5%E3%82%89%E3%81%9B-f78a1421fe08) (Tokyo, Japan) — October 29–30 + +* **[Google for Developers events ](https://developers.google.com/community)(DevFest and Build with AI)** — Throughout Fall and Winter 2026. + +Stay tuned and bookmark Flutter.dev’s [Events](https://flutter.dev/events) page as we continue to add more events throughout the year. + +**Are you hosting a Flutter event or meetup not listed here? Reach [out to our team](mailto:dash-devrel@google.com) to see if we can attend if we’re already in your area — we want to meet and share with as many of you as possible!** \ No newline at end of file diff --git a/sites/www/content/blog/creating-a-carousel-with-flutter/images/0-y8UdKzHXj8SEouz.jpg b/sites/www/content/blog/creating-a-carousel-with-flutter/images/0-y8UdKzHXj8SEouz.jpg new file mode 100644 index 0000000000..0e29848aa4 Binary files /dev/null and b/sites/www/content/blog/creating-a-carousel-with-flutter/images/0-y8UdKzHXj8SEouz.jpg differ diff --git a/sites/www/content/blog/creating-a-carousel-with-flutter/images/13fBpctMQDW1Grz94Brr4jg.webp b/sites/www/content/blog/creating-a-carousel-with-flutter/images/13fBpctMQDW1Grz94Brr4jg.webp new file mode 100644 index 0000000000..c5c3bab20e Binary files /dev/null and b/sites/www/content/blog/creating-a-carousel-with-flutter/images/13fBpctMQDW1Grz94Brr4jg.webp differ diff --git a/sites/www/content/blog/creating-a-carousel-with-flutter/images/14936vpzIKoyCsk1z0_Xk4w.webp b/sites/www/content/blog/creating-a-carousel-with-flutter/images/14936vpzIKoyCsk1z0_Xk4w.webp new file mode 100644 index 0000000000..1c274b9f6a Binary files /dev/null and b/sites/www/content/blog/creating-a-carousel-with-flutter/images/14936vpzIKoyCsk1z0_Xk4w.webp differ diff --git a/sites/www/content/blog/creating-a-carousel-with-flutter/images/1BRF6ceZiW5XXgx78k1L3eQ.webp b/sites/www/content/blog/creating-a-carousel-with-flutter/images/1BRF6ceZiW5XXgx78k1L3eQ.webp new file mode 100644 index 0000000000..4fd749b232 Binary files /dev/null and b/sites/www/content/blog/creating-a-carousel-with-flutter/images/1BRF6ceZiW5XXgx78k1L3eQ.webp differ diff --git a/sites/www/content/blog/creating-a-carousel-with-flutter/images/1Q5zBvfcEWWWcHUOlnl7tZw.webp b/sites/www/content/blog/creating-a-carousel-with-flutter/images/1Q5zBvfcEWWWcHUOlnl7tZw.webp new file mode 100644 index 0000000000..9eb8b0f9c7 Binary files /dev/null and b/sites/www/content/blog/creating-a-carousel-with-flutter/images/1Q5zBvfcEWWWcHUOlnl7tZw.webp differ diff --git a/sites/www/content/blog/creating-a-carousel-with-flutter/images/1Tqu0lBJy5DfrmgsxjixEwg.webp b/sites/www/content/blog/creating-a-carousel-with-flutter/images/1Tqu0lBJy5DfrmgsxjixEwg.webp new file mode 100644 index 0000000000..be5fb35fe4 Binary files /dev/null and b/sites/www/content/blog/creating-a-carousel-with-flutter/images/1Tqu0lBJy5DfrmgsxjixEwg.webp differ diff --git a/sites/www/content/blog/creating-a-carousel-with-flutter/images/1XUFqw8TZ281xRUTvp6EERg.webp b/sites/www/content/blog/creating-a-carousel-with-flutter/images/1XUFqw8TZ281xRUTvp6EERg.webp new file mode 100644 index 0000000000..7309f3c174 Binary files /dev/null and b/sites/www/content/blog/creating-a-carousel-with-flutter/images/1XUFqw8TZ281xRUTvp6EERg.webp differ diff --git a/sites/www/content/blog/creating-a-carousel-with-flutter/images/1_CXIYvhlix6kCN5PaySSdw.webp b/sites/www/content/blog/creating-a-carousel-with-flutter/images/1_CXIYvhlix6kCN5PaySSdw.webp new file mode 100644 index 0000000000..773c772980 Binary files /dev/null and b/sites/www/content/blog/creating-a-carousel-with-flutter/images/1_CXIYvhlix6kCN5PaySSdw.webp differ diff --git a/sites/www/content/blog/creating-a-carousel-with-flutter/images/1cO4d8OZCrc3jwJnIWw4Lmw.webp b/sites/www/content/blog/creating-a-carousel-with-flutter/images/1cO4d8OZCrc3jwJnIWw4Lmw.webp new file mode 100644 index 0000000000..fe48b41bb0 Binary files /dev/null and b/sites/www/content/blog/creating-a-carousel-with-flutter/images/1cO4d8OZCrc3jwJnIWw4Lmw.webp differ diff --git a/sites/www/content/blog/creating-a-carousel-with-flutter/images/1my7PRln0pZA7N9WytaYIog.webp b/sites/www/content/blog/creating-a-carousel-with-flutter/images/1my7PRln0pZA7N9WytaYIog.webp new file mode 100644 index 0000000000..95094d8b76 Binary files /dev/null and b/sites/www/content/blog/creating-a-carousel-with-flutter/images/1my7PRln0pZA7N9WytaYIog.webp differ diff --git a/sites/www/content/blog/creating-a-carousel-with-flutter/index.md b/sites/www/content/blog/creating-a-carousel-with-flutter/index.md new file mode 100644 index 0000000000..72a5abc0a8 --- /dev/null +++ b/sites/www/content/blog/creating-a-carousel-with-flutter/index.md @@ -0,0 +1,259 @@ +--- +title: "Creating a Carousel with Flutter" +description: "As a mobile developer, you may find yourself with a design that calls for sliding, animated, background-image carousels. In this article, I…" +publishDate: 2018-04-09 +author: ezra-sandzer-bell +image: images/0-y8UdKzHXj8SEouz.jpg +category: tutorial +layout: blog +--- + +<DashImage figure src="images/0-y8UdKzHXj8SEouz.jpg" alt="Creating Background Image Carousels with Flutter" caption="Creating Background Image Carousels with Flutter" /> + + +As a mobile developer, you may find yourself with a design that calls for sliding, animated, background-image carousels. In this article, I will review some of the existing documentation for implementing a carousel in Flutter, and fill you in on the information gaps so that you have everything you need to get started! + +If you are brand new to Flutter, please begin with the setup process: [https://flutter.io/get-started/](https://flutter.io/get-started/) + +The Carousel plugin we will be working with was published January 21, 2018 by Gabriel Valério: [https://pub.dartlang.org/packages/carousel](https://pub.dartlang.org/packages/carousel) + +You can follow along with this article to create the project from scratch, but if you want the fast-track, I’ve posted the complete project on github: [https://github.com/ezrasandzerbell/FLUTTER-carousel-demo](https://github.com/ezrasandzerbell/FLUTTER-carousel-demo) + +I will be using Android Studio for this walkthrough. Your first step will be to create a new Flutter Project. When the project has been created, navigate to the **pubspec.yaml** file located at the top level of your project directory: + +``` +**name:** carousel_demo +**description:** Carousel_Demo + +**dependencies: + flutter: + sdk:** flutter + **carousel:** ^0.1.0 +``` + + +As shown above, you will need to add “**carousel: ^0.1.0**” directly under the flutter SDK dependency. This communicates to the app that you want to add carousel version 0.1.0. The caret (^) prefix indicates that minor patch updates to the specified version are permitted. You can read about the caret prefix [here](https://bytearcher.com/articles/semver-explained-why-theres-a-caret-in-my-package-json/). + +<DashImage figure src="images/1my7PRln0pZA7N9WytaYIog.webp" alt="Click “Packages get” after declaring the carousel dependency in your pubspec" caption="Click “Packages get” after declaring the carousel dependency in your pubspec" /> + + +Next, run **flutter packages get.** If you are in Android Studio, you can do this by clicking the hyperlink that sits directly above the pubspec.yaml file. Note that you must be viewing this file specifically to run **packages get.** + +Once the package has been installed, navigate to your **main.dart** file where your landing page code currently lives. Delete everything that’s there and replace it with this barebones Hello World script. Notice that we are **importing carousel.dart** at the top of the file: + +``` +**import** 'package:flutter/material.dart'; +**import** 'package:carousel/carousel.dart'; + +void main() => runApp(new MyApp()); + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return new MaterialApp( + title: 'Flutter Carousel', + home: new Scaffold( + appBar: new AppBar( + title: new Text('Carousel Demo'), + ), + body: new Center( + child: new Text('Hello World'), + ), + ), + ); + } +} +``` + + +You can go ahead and run the app at this point to ensure that everything is working as expected. In Android Studio, this is done by clicking the green play button. Once you have started the app, you can press **command+shift+s** to reload the app at any time. + +<DashImage figure src="images/13fBpctMQDW1Grz94Brr4jg.webp" alt="Click the Green Play button to run the app. In this example we are using the iOS Simulator." caption="Click the Green Play button to run the app. In this example we are using the iOS Simulator." /> + + +<DashImage figure src="images/1Tqu0lBJy5DfrmgsxjixEwg.webp" alt="This is what you should see upon loading the app." caption="This is what you should see upon loading the app." /> + + +Let’s begin implementing the carousel feature. Your body value will be replaced with a Stack that has two children: a **PageView** and a **ListView**. + +The **PageView** will handle our background image, while the ListView holds our “Hello World” text. + +``` +body: **new** Stack ( + children: <Widget>[ + **new** PageView( + children: [testBGCarousel], + ), + **new** ListView( + children: [ + **new** Text('This text displays on top of the carousel'), + ], + ), + ] +), +``` + + +Of course, testBGCarousel does not yet exist. In order to load an image into the background, we will have to create a new asset folder. Right click on the project folder and select New > Directory. You can name the directory “images” or something comparable. + +<DashImage figure src="images/1_CXIYvhlix6kCN5PaySSdw.webp" /> + + +You can grab three background images at random for the purposes of testing. I like to format them ahead of time so they sit well in the app. Consider using 1000x1472 px with 72 dpi .jpg format. Place these images into the **images** folder that you created. + +<DashImage figure src="images/1Q5zBvfcEWWWcHUOlnl7tZw.webp" /> + + +Just to make this extra simple, here are three full-size color templates you can download and use to run this code sample: + +<DashImage figure src="images/1XUFqw8TZ281xRUTvp6EERg.webp" /> + + +<DashImage figure src="images/14936vpzIKoyCsk1z0_Xk4w.webp" /> + + +<DashImage figure src="images/1BRF6ceZiW5XXgx78k1L3eQ.webp" alt="Download these three images and use them to test the carousel." caption="Download these three images and use them to test the carousel." /> + + +``` + **** **new** ListView( + children: [ + **new** Text('This text displays on top of the carousel'), + ], + ), +``` + + +The aforementioned **ListView** is set up with a single child argument of new Text. I am featuring the ListView here as a placeholder, simply to demonstrate where you would place your list of widgets that sit on top of the PageView. ListViews can hold any variety of children, including Images, form fields, buttons, and so forth. + +Next, in your **pubspec.yaml** file, declare these assets like this: + +``` +**name:** carousel_demo +**description:** Carousel_Demo + +**dependencies: + flutter: + sdk:** flutter + **carousel:** ^0.1.0 + ****cupertino_icons:** ^0.1.0 + +**dev_dependencies: + flutter_test: + sdk:** flutter + +****flutter:** + ****uses-material-design:** true + **assets:** + - images/img1.jpg + - images/img2.jpg + - images/img3.jpg +``` + + +This makes the image assets available to your app. Back in the **main.dart** file, add this carousel code to introduce the images: + +``` +**class** MyApp **extends** StatelessWidget { + @override + + Widget testBGCarousel = **new** Container( + child: **new** Carousel( + children: [ + **new** AssetImage('images/img1.jpg'), + **new** AssetImage('images/img2.jpg'), + **new** AssetImage('images/img3.jpg'), + ].map((bgImg) => **new** Image(image: bgImg, width: 1500.0, height: 1500.0, fit: BoxFit.cover)).toList(), + displayDuration: **const** Duration(seconds: 1), + ), + ); + + + Widget build(BuildContext context) { + **return new** MaterialApp( ... )} +``` + + +I have abbreviated the Widget build to keep the code snippet short. All you need to add in is the testBGCarousel widget. Keep reading for a breakdown on how it works: + +**The Children List:** You can add as many AssetImages as you want, provided you keep the correct format. Follow the syntax shown above to access the .jpg files in the image folder you created. Note that ***arrays** are called [**lists**](https://api.dartlang.org/stable/1.24.3/dart-core/List-class.html) in Dart*. + +**The Map Method:** Mapping is similar to a forEach loop; it takes each element of a list and performs some action. You can read up on Dart’s map method [here](https://api.dartlang.org/stable/1.24.3/dart-core/Iterable/map.html). + +``` +.map((bgImg) => **new** Image(image: bgImg, width: 1500.0, height: 1500.0, fit: BoxFit.cover)).toList() +``` + + +We are mapping over the list of AssetImages one at a time. The first argument (bgImg) holds the value of the current AssetImage. Next, the fat arrow (=>) executes a function where the value of *bgImg* is positioned within a new context. That context is the new Image(…).toList() statement. + +**Each New Image:** The first property, *image,* receives the mapped element *bgImg* as a value. Additional properties are assigned. Note that I am using **fit: BoxFit.cover** to automatically fit the images within the box. The large width and height values ensure image scales into a large enough space. Once the new Image is created, the .toList() method pushes that image to a new version of the Carousel’s children List. + +``` +displayDuration: **const** Duration(seconds: 1), +``` + + +Below the children, the Carousel receives an additional, optional argument on the property **displayDuration** that changes the speed at which the carousel changes images. I added this for easy reference in case you want to do further customization. For additional properties that live on this Carousel widget, check out the **carousel.dart** file by holding the **command** key and left-clicking on the word Carousel within your file: + +<DashImage figure src="images/1cO4d8OZCrc3jwJnIWw4Lmw.webp" alt="Command + Click the word Carousel to view carousel.dart" caption="Command + Click the word Carousel to view carousel.dart" /> + + +``` +Carousel({ + **this**.children, + **this**.animationCurve = Curves.*ease*, + **this**.animationDuration = **const** Duration(milliseconds: 250), + **this**.displayDuration = **const** Duration(seconds: 2) +}) : +``` + + +To customize animation duration and curve, command + click on the property names to view your options. When you know what you want, you can add the new properties following the *displayDuration* format shown in the testBGCarousel example. + +Reload your app and voila, you have a working carousel. Here is the complete code: + +``` +**import** 'package:flutter/material.dart'; +**import** 'package:carousel/carousel.dart'; + +void main() => runApp(new MyApp()); + +class MyApp extends StatelessWidget { + @override + +Widget testBGCarousel = new Container( + child: new Carousel( + children: [ + new AssetImage('images/img1.jpg'), + new AssetImage('images/img2.jpg'), + new AssetImage('images/img3.jpg'), + ].map((bgImg) => new Image(image: bgImg, width: 1500.0, height: 1500.0, fit: BoxFit.cover)).toList(), + displayDuration: const Duration(seconds: 1), + ), + ); + + Widget build(BuildContext context) { + return new MaterialApp( + title: 'Flutter Carousel', + home: new Scaffold( + appBar: new AppBar( + title: new Text('Carousel Demo'), + ), + body: new Stack ( + children: <Widget>[ + new PageView( + children: [testBGCarousel], + ), + new ListView( + children: [ + new Text('Text on top of the carousel'), + ], + ), + ] + ), + ), + ); + } +} +``` diff --git a/sites/www/content/blog/cross-platform-development-with-flutter-how-google-classroom-gets-teachers-and-students-on-the/images/1-u0y5vTqoC_sCyOHpvUnNA.webp b/sites/www/content/blog/cross-platform-development-with-flutter-how-google-classroom-gets-teachers-and-students-on-the/images/1-u0y5vTqoC_sCyOHpvUnNA.webp new file mode 100644 index 0000000000..0bd85b706f Binary files /dev/null and b/sites/www/content/blog/cross-platform-development-with-flutter-how-google-classroom-gets-teachers-and-students-on-the/images/1-u0y5vTqoC_sCyOHpvUnNA.webp differ diff --git a/sites/www/content/blog/cross-platform-development-with-flutter-how-google-classroom-gets-teachers-and-students-on-the/images/1JS2z5_kG8Z8hAfbrxeOtCw.webp b/sites/www/content/blog/cross-platform-development-with-flutter-how-google-classroom-gets-teachers-and-students-on-the/images/1JS2z5_kG8Z8hAfbrxeOtCw.webp new file mode 100644 index 0000000000..5f4ab2952f Binary files /dev/null and b/sites/www/content/blog/cross-platform-development-with-flutter-how-google-classroom-gets-teachers-and-students-on-the/images/1JS2z5_kG8Z8hAfbrxeOtCw.webp differ diff --git a/sites/www/content/blog/cross-platform-development-with-flutter-how-google-classroom-gets-teachers-and-students-on-the/images/1slexehY5Bz7L36tuIuxP4g.webp b/sites/www/content/blog/cross-platform-development-with-flutter-how-google-classroom-gets-teachers-and-students-on-the/images/1slexehY5Bz7L36tuIuxP4g.webp new file mode 100644 index 0000000000..fa2c311aae Binary files /dev/null and b/sites/www/content/blog/cross-platform-development-with-flutter-how-google-classroom-gets-teachers-and-students-on-the/images/1slexehY5Bz7L36tuIuxP4g.webp differ diff --git a/sites/www/content/blog/cross-platform-development-with-flutter-how-google-classroom-gets-teachers-and-students-on-the/images/1yJhbJRuTHa2wt-ofA9fazw.webp b/sites/www/content/blog/cross-platform-development-with-flutter-how-google-classroom-gets-teachers-and-students-on-the/images/1yJhbJRuTHa2wt-ofA9fazw.webp new file mode 100644 index 0000000000..52ca2d8149 Binary files /dev/null and b/sites/www/content/blog/cross-platform-development-with-flutter-how-google-classroom-gets-teachers-and-students-on-the/images/1yJhbJRuTHa2wt-ofA9fazw.webp differ diff --git a/sites/www/content/blog/cross-platform-development-with-flutter-how-google-classroom-gets-teachers-and-students-on-the/index.md b/sites/www/content/blog/cross-platform-development-with-flutter-how-google-classroom-gets-teachers-and-students-on-the/index.md new file mode 100644 index 0000000000..0558a1073b --- /dev/null +++ b/sites/www/content/blog/cross-platform-development-with-flutter-how-google-classroom-gets-teachers-and-students-on-the/index.md @@ -0,0 +1,110 @@ +--- +title: "Cross Platform development with Flutter — How Google Classroom gets teachers and students on the…" +description: "Google’s Classroom app, which originally launched in 2014, is used by 150 million educators and students around the world to organize…" +publishDate: 2024-05-07 +author: craiglabenz +image: images/1-u0y5vTqoC_sCyOHpvUnNA.webp +category: spotlight +layout: blog +--- + +## Cross Platform development with Flutter — How Google Classroom gets teachers and students on the same page + +Google’s Classroom app, which originally launched in 2014, is used by 150 million educators and students around the world to organize homework, grades, and communication in the classroom. Available on Android and iOS, development originally began earlier that year and has spanned eras of tremendous change on both mobile platforms. Managing that divergent change, it turned out, was challenging. + +Despite efforts toward synchronization, 7 years later in 2021, the Classroom app’s distinct Android and iOS codebases had incrementally drifted apart in features, UI, and implementation. From the most obvious, like screens that took different approaches to the same UI, to the less obvious, like authentication and app start-up logic differences; Classroom had slowly turned into a difficult app to maintain and improve, with two codebases taxing a small developer pool. + +A spectrum of options were available, ranging from persevering with the status quo, to adding more developers, to a complete rewrite of both codebases with a cross-platform framework. The team was committed to improvement, which ruled out the status quo option, and then assessed what it would take to stabilize the two existing codebases, which ruled out merely adding more developers. In the end, the team settled on its third choice: reimagine Classroom with a single-source, cross-platform solution, which was ultimately Flutter. + +## How Flutter simplified the Classroom app + +### Inconsistent UIs + +The most visible of Classroom’s issues — UI variations — forced teachers to intimately know both the Android and iOS UIs. After all, it’s easy to imagine students asking questions about this homework screen and instructions from one platform’s perspective not making much sense against what a student was seeing on their device from the other platform. + +<DashImage figure src="images/1-u0y5vTqoC_sCyOHpvUnNA.webp" /> + + +The conventional approach is for separate client apps, developed by separate teams, that diverge over time. Only consistent, painstaking work to synchronize every feature can prevent this. In contrast, Flutter’s very nature inverts this default outcome. With Flutter, UIs are the same by default [1] until active work ([often in pursuit of adaptability](https://medium.com/flutter/extreme-ui-adaptability-in-flutter-how-google-earth-supports-every-use-case-on-earth-6db4661e7a17)) forces them to diverge for the benefit of the user. +> [1] In Classroom’s Flutter client, small, intentional differences remain, like system bars and bottom controls. Flutter preserves those idiomatic platform details while leaving the middle 90% of the screen to be filled by a single UI implementation. + +### Muddled business logic + +Estranged UIs were not the only place where Classroom’s Android and iOS clients were at odds. Shaped by a server-side solution that offloaded some complex business logic to the client, the old Classroom app also dealt with differences across core implementations. Aside from giving rise to the occasional platform-specific bug (which can frustrate an engineer’s attempt to reproduce!), this imposed a considerable mental tax on anyone assessing the correctness of either implementation. + +Rewriting Classroom with Flutter solved a slew of bugs, both previously reported and not, simply because of how Flutter handles native platform interactions. + +In the original code, years of constant development had occasionally blurred the lines between UI, business, and platform-specific logic. This meant that users’ bug reports were almost always a tremendous effort to isolate, as the whole call stack was potentially guilty. Was a requested file not loading because the file system was being read incorrectly, because of a miscommunication in business logic, or because the UI received the file, but then lost track of it? The only way to find out was to *investigate everything*. + +Of course, Flutter developers can blur these lines and mix logic just like anyone else, but the Classroom engineering team found that following framework best practices made attempts to do so painfully obvious. Flutter’s system of declarative UI strongly advises against accidentally placing business logic within UI widgets, and a new MVVM architecture even helped enforce clear layers of responsibility within the vast codebase that sits behind Flutter widgets. + +Flutter apps still periodically talk to the underlying platform — after all, the user journey of uploading and viewing homework can’t happen without using the file system — but here again Flutter’s pattern of quarantining platform-specific logic into dedicated plugins prevents something like routine disk I/O from sneaking into places where it doesn’t belong. The following example shows a realistic way Flutter apps access the file system without muddying up the whole call stack. + +```dart +import "dart:io"; +import "package:path/path.dart" as path; +import "package:path_provider/path_provider.dart" as path_provider; + +// Loads the student's saved homework for a given assignment. +// Return value's exists() function will return False if the +// student's dog ate their homework. +Future<File> getHomework(Assignment assignment) async { + // Use the `path_provider` package to abstract away platform-specific + // file system quirks + final Directory homeworkDirectory = + await path_provider.getApplicationSupportDirectory(); + + // Extract the student's uploaded homework + return File( + path.join([homeworkDirectory.absolute.path, assignment.name]), + ); +} +``` + + +This example is simple, and the Classroom engineering team ultimately developed several of their own plugins to contain more complicated interactions with the host platform. Interestingly, doing so made their native code *easier to debug* than it had been in their original native apps. How was this possible? Following Don’t Repeat Yourself (DRY) principles in Flutter plugins means hoisting as much business logic as possible into Dart code, leaving only the simplest in-and-out method calls for native interactions. This forces a rock-solid separation between domain logic and platform logic; meaning that any errors in Classroom’s Android or iOS code were probably in isolated, single-responsibility functions that were easily reasoned about. + +### Performance drains + +When a user journey fails, a concrete bug is filed that everyone can agree needs to be fixed. But what happens for softer problems, like slow app startup time that has been getting worse year after year since an app’s launch? Sprinkle in concerns about keeping multiple clients in sync, and suddenly troubleshooting an app’s lethargic bootstrapping flow feels like a hopeless task. + +Here, Flutter helped by being fast enough itself to not make the problem worse and, more importantly, by offering the chance at a clean slate. Knowing they were building something new instead of working around the skeletons from years of development, the Classroom team crystallized their authorization and bootstrapping flows by removing redundant API calls, parallelizing other independent API calls, and showing shimmer effects and other UI previews while everything resolved. The result was a staggering *80% reduction in app launch time!* + +### Annotation feature + +Most of Classroom’s functionality could be seen as a fairly routine app bringing together users around shared content, like the assignments and uploaded homework. But, one feature stands out as *obviously tricky*. One of Classroom’s banner features is file sharing, where both teachers and students can create, view, and edit files, including free-form annotation as if drawing on real paper with a pen or marker. This annotation-sharing feature already existed in Classroom’s native Android and iOS clients, so any friction porting it to Flutter would be a deal-breaker. + +The Classroom team was able to re-package this annotation functionality in a plugin that delegates platform-specific implementations to separate libraries. For file annotation, those became thin wrappers around the pre-existing native libraries already used in Google One, Google Keep, and the old Classroom app. Internally, Android and iOS have different implementation requirements around file-sharing. On iOS, the Classroom app accesses files through native views, but on Android it opens the Google Keep app directly. However, good plugin design principles were able to isolate these implementation details and still expose a clean, singular Dart API for the rest of the app to navigate. In the end, one of Classroom’s “trickiest” features had been successfully ported to Flutter. + +Below is a visualization of Classroom’s annotation feature on Android, outlining the mixture of native and Flutter UI components. + +<DashImage figure src="images/1yJhbJRuTHa2wt-ofA9fazw.webp" alt="Four mobile screens side by side, collectively showing the user flow to select and annotate a file" caption="Four mobile screens side by side, collectively showing the user flow to select and annotate a file" /> + + +More broadly, typical plugin design in Flutter looks like the following, where a single, streamlined interface loads platform-specific libraries which, in turn, use FFI or JNI to talk to the underlying platform. This allows Flutter apps to engage with platform-specific native APIs across all of their build targets without leaking those considerations into Dart code. + +<DashImage figure src="images/1slexehY5Bz7L36tuIuxP4g.webp" alt="A diagram of how to design a Flutter plugin" caption="A diagram of how to design a Flutter plugin" /> + + +## Looking back + +### Developer velocity + +The Classroom team spent 2 years rewriting their app with a team that grew from 1 engineer (for the initial prototyping phase) to 10 full-time engineers at peak development. This is no small investment, but the team made it on the promise of faster development and maintenance in perpetuity. Classroom launched their Flutter rewrite on iOS in June, 2023 and completed the project by launching on Android in January, 2024. Since then, average engineering time spent on new features has decreased by two-thirds, which is a tripling of developer velocity! After waiting 2 years for any new features, stakeholders have been delighted by the long-awaited arrival of ✨faster feature development✨. + +Part of the Classroom team’s decision to rewrite was, knowing that their project would never be “done”, new features were likely to be added long into the future. This allowed for a compelling case that a rewrite, even an expensive one, would eventually pay for itself. The formula for when the Classroom team would hit the break-even point on investment from their rewrite is: + +<DashImage figure src="images/1JS2z5_kG8Z8hAfbrxeOtCw.webp" alt="The formula for estimating when increased velocity from rewriting an app in Flutter will exceed the time spent rewriting. The formula is time spent rewriting divided by the reduction in time spent writing features in Flutter equals the number of post-launch features to break even." caption="The formula for estimating when increased velocity from rewriting an app in Flutter will exceed the time spent rewriting. The formula is time spent rewriting divided by the reduction in time spent writing features in Flutter equals the number of post-launch features to break even." /> + + +In the 9 months since launching iOS, Classroom estimates to have already reclaimed 40% of that initial investment through the tripling of developer velocity offered by Flutter. + +### Developer experience + +The only people made happier by increased developer velocity than stakeholders is — *the developers themselves*. For the Classroom team, their 3x increase in velocity came from a mixture of only writing each feature once (or at most, 1.5 times in scenarios with heavy native components), eliminating the costs of coordination for two teams whose schedules were often months apart and, of course, hot reload. Hot reload alone, with its ~99% reduction in rebuild times, buoyed the Classroom team’s morale above where it had ever been with their two native clients. In the end, the Classroom team found it easy to attract and retain engineers after their switch to Flutter. + +Additionally, the Classroom team found that new features, on average, required at least 50% fewer lines of code to implement. In reality, the decrease is likely quite a bit higher, since not every feature that they built during the rewrite was even properly implemented in the two native clients! Put differently, 50% as many lines of code delivered all the old features, plus coverage of *large* feature gaps (including offline support on iOS). + +## Conclusion + +Roughly two years after beginning their rewrite, the Classroom team has shipped their app to Android and iOS and added enough additional features to pay down 40% of their upfront investment. Their new app launches almost *five times as fast* as the old, saving both developers and end-users time and frustration. Looking forward, new features average one-third the development cost in comparison to the old status quo, enjoy simultaneous release on both platforms, *and* are easier to troubleshoot and maintain. Ultimately, user, developer, and stakeholder morale has never been higher than it became after Classroom switched to Flutter to reinvest in their future. \ No newline at end of file diff --git a/sites/www/content/blog/custom-implicit-animations-in-flutter-with-tweenanimationbuilder/images/11-TXNn5MMPC7MDr4OZd-EA.webp b/sites/www/content/blog/custom-implicit-animations-in-flutter-with-tweenanimationbuilder/images/11-TXNn5MMPC7MDr4OZd-EA.webp new file mode 100644 index 0000000000..d206fd564a Binary files /dev/null and b/sites/www/content/blog/custom-implicit-animations-in-flutter-with-tweenanimationbuilder/images/11-TXNn5MMPC7MDr4OZd-EA.webp differ diff --git a/sites/www/content/blog/custom-implicit-animations-in-flutter-with-tweenanimationbuilder/images/11wJWHHPYfOYUCDBAcmIpjg.webp b/sites/www/content/blog/custom-implicit-animations-in-flutter-with-tweenanimationbuilder/images/11wJWHHPYfOYUCDBAcmIpjg.webp new file mode 100644 index 0000000000..fcb088e981 Binary files /dev/null and b/sites/www/content/blog/custom-implicit-animations-in-flutter-with-tweenanimationbuilder/images/11wJWHHPYfOYUCDBAcmIpjg.webp differ diff --git a/sites/www/content/blog/custom-implicit-animations-in-flutter-with-tweenanimationbuilder/images/17xwzsHM8FSwtXlWP_3q9qA.webp b/sites/www/content/blog/custom-implicit-animations-in-flutter-with-tweenanimationbuilder/images/17xwzsHM8FSwtXlWP_3q9qA.webp new file mode 100644 index 0000000000..3b4f8f5415 Binary files /dev/null and b/sites/www/content/blog/custom-implicit-animations-in-flutter-with-tweenanimationbuilder/images/17xwzsHM8FSwtXlWP_3q9qA.webp differ diff --git a/sites/www/content/blog/custom-implicit-animations-in-flutter-with-tweenanimationbuilder/images/1ZiMTAgg11y3BHH24bTt6Mg.gif b/sites/www/content/blog/custom-implicit-animations-in-flutter-with-tweenanimationbuilder/images/1ZiMTAgg11y3BHH24bTt6Mg.gif new file mode 100644 index 0000000000..dcdc03761c Binary files /dev/null and b/sites/www/content/blog/custom-implicit-animations-in-flutter-with-tweenanimationbuilder/images/1ZiMTAgg11y3BHH24bTt6Mg.gif differ diff --git a/sites/www/content/blog/custom-implicit-animations-in-flutter-with-tweenanimationbuilder/images/1_JRebndf4uwMwxVzyNunYg.webp b/sites/www/content/blog/custom-implicit-animations-in-flutter-with-tweenanimationbuilder/images/1_JRebndf4uwMwxVzyNunYg.webp new file mode 100644 index 0000000000..3195474bc1 Binary files /dev/null and b/sites/www/content/blog/custom-implicit-animations-in-flutter-with-tweenanimationbuilder/images/1_JRebndf4uwMwxVzyNunYg.webp differ diff --git a/sites/www/content/blog/custom-implicit-animations-in-flutter-with-tweenanimationbuilder/index.md b/sites/www/content/blog/custom-implicit-animations-in-flutter-with-tweenanimationbuilder/index.md new file mode 100644 index 0000000000..e5b7a39d0a --- /dev/null +++ b/sites/www/content/blog/custom-implicit-animations-in-flutter-with-tweenanimationbuilder/index.md @@ -0,0 +1,325 @@ +--- +title: "Custom Implicit Animations in Flutter…with TweenAnimationBuilder" +description: "To make animations in Flutter, there are many different options available. How do you choose the right animation widget? This article…" +publishDate: 2019-12-15 +author: efortuna +image: images/1ZiMTAgg11y3BHH24bTt6Mg.gif +category: announcements +layout: blog +--- + +To make animations in Flutter, there are many different options available. How do you choose the right animation widget? This article, [part](https://medium.com/flutter/flutter-animation-basics-with-implicit-animations-95db481c5916) of [our](https://medium.com/flutter/directional-animations-with-built-in-explicit-animations-3e7c5e6fbbd7) animation series, delves into the best use cases for `TweenAnimationBuilder` versus when a different widget is best for the job. The previous article in this series explains just how powerful all the built-in implicit animation widgets are and how much you can accomplish with them. + +<DashImage figure src="images/11wJWHHPYfOYUCDBAcmIpjg.webp" alt="A flow chart for understanding what animation to use in any scenario. This article focuses on the second-from-the-right “end state”, TweenAnimationBuilder." caption="A flow chart for understanding what animation to use in any scenario. This article focuses on the second-from-the-right “end state”, TweenAnimationBuilder." /> + + +Why use `TweenAnimationBuilder`? Suppose you want to create a *basic* animation: an animation that *doesn’t* repeat forever and is just one widget or widget tree. Flutter has a convention of naming its implicitly animated widgets `AnimatedFoo`, where `Foo` is the name of the property that animates. Don’t believe me? Here’s a sample of built-in, implicitly animated widgets: `AnimatedContainer`, `AnimatedCrossFade`, `AnimatedDefaultTextStyle`, `AnimatedModalBarrier`, `AnimatedOpacity`, `AnimatedPadding`, `AnimatedPhysicalModel`, `AnimatedPositioned`, `AnimatedPositionedDirectional`, `AnimatedSwitcher`. This set of widgets is impressively powerful, and you can accomplish a lot of your needs just using those. `AnimatedContainer` can even let you animate gradients and rotate widgets, all without needing to worry about an `AnimationController`! + +However, if you need to create a basic animation and none of those built-in implicit animations are what you’re looking for, you can still create that animation with `TweenAnimationBuilder`! + +## The basics + +To use `TweenAnimationBuilder`, I set the length of time that I want my animation to take with the duration parameter, and the range of values that I want it to animate between with the…`Tween` parameter. As the name suggests, a `Tween` object enables you to specify a range of values that you want to animate *be**tween***. + +The last thing I need to specify is the builder parameter, which returns what my animated widget will look like at a given moment in time. This builder function takes a parameter that is the same type as your `Tween` values, which basically tells Flutter what the current animation value is at a given moment. + +```dart +/// This is an EXTREMELY bare-bones illustration of using TweenAnimationBuilder. +/// See the rest of the article for optimizations. +/// Also note that this example is for illusration use only -- an implicit rotation +/// animation can be accomplished with AnimatedContainer. +class SuperBasic extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Stack( + children: <Widget>[ + starsBackground, + Center( + child: TweenAnimationBuilder<double>( + tween: Tween<double>(begin: 0, end: 2 * math.pi), + duration: Duration(seconds: 2), + builder: (BuildContext context, double angle, Widget child) { + return Transform.rotate( + angle: angle, + child: Image.asset('assets/Earth.png'), + ); + }, + ), + ), + ], + ); + } +} +``` + +## TweenAnimationBuilder in depth + +The example code above showed the bare-minimum set of arguments necessary to use `TweenAnimationBuilder`, but there is a lot more this widget has to offer! For illustration purposes, I created an app for an extremely common use case: illustration of the Doppler effect in space. Okay, it’s a silly use case, but you might want to apply a color filter to an image and animate the changing colors…which is exactly what we’ll be doing in this scenario. + +In the Doppler effect, when a star moves away from you in space, the waves of light elongate, making the light shift closer to the red end of the spectrum. This effect is very subtle and not visible to the naked eye, but astronomers use it to determine the velocity of stars and galaxies relative to us. + +<DashImage figure src="images/11-TXNn5MMPC7MDr4OZd-EA.webp" alt="Consult your local astrophysicist for more details." caption="Consult your local astrophysicist for more details." /> + + +In our app we’re going to make this a little less subtle. I have a nice image of a star, and to change its color, I’m going to use the `ColorFiltered` widget. I apply a blend mode, and tell it to blend orange into the image to make it a little more reddish. + +``` +ColorFiltered( + child: Image.asset('assets/sun.png'), + colorFilter: ColorFilter.mode(color, BlendMode.modulate), +) +``` + + +Next step…animation! There isn’t a built-in widget that applies an arbitrary color filter to a widget, but we can build one ourselves with `TweenAnimationBuilder`. To change the color over time, we want to modify the color that we’re applying to the filter. So that’s the value that we’ll animate. We’ll put the `ColorFiltered` widget inside the builder function of the `TweenAnimationBuilder`. As I mentioned before, a `Tween` is just the range of values that we are animating between. In this case, we’ll use a `ColorTween` to animate between white, which is as if we had no filter, and orange. And there you have it! A nicely animated color filter in 10 lines of code. + +``` +TweenAnimationBuilder( + tween: ColorTween(begin: Colors.white, end: Colors.red), + duration: Duration(seconds: 2), + builder: (_, Color color, __) { + return ColorFiltered( + child: Image.asset('assets/sun.png'), + colorFilter: ColorFilter.mode(color, BlendMode.modulate), + ); + }, +) +``` + + +<DashImage figure src="images/1_JRebndf4uwMwxVzyNunYg.webp" /> + + +Depending on *what* you want to animate though, your `Tween` can specify ranges between things other than colors or numbers. You can have a `Tween` with `Offset` objects to animate the change of a widget’s position, or you can even animate how the border of a widget changes! The point is you have a ton of options. + +Tweens are mutable, so if you know that you’re always going to animate between the same set of values, it’s best to declare your `Tween` as a static final variable in your class. That way, you don’t create a new object every time you rebuild. + +```dart +class ColorAnimationWithStaticFinal extends StatelessWidget { + static final colorTween = ColorTween(begin: Colors.white, end: Colors.red); + + @override + Widget build(BuildContext context) { + return Stack( + children: <Widget>[ + starsBackground, + Center( + child: TweenAnimationBuilder<Color>( + tween: colorTween, + duration: Duration(seconds: 2), + builder: (_, Color color, __) { + return ColorFiltered( + child: Image.asset('assets/sun.png'), + colorFilter: ColorFilter.mode(color, BlendMode.modulate), + ); + }, + ), + ), + ], + ); + } +} +``` + +## Dynamically modifying Tween values + +The previous example showed a really simple way to animate from one value to another without using `setState` or anything. But, you can do more with `TweenAnimationBuilder` by *dynamically modifying* your `Tween` value. + +```dart +class OngoingAnimationByModifyingEndTweenValue extends StatefulWidget { + @override + _OngoingAnimationState createState() => _OngoingAnimationState(); +} + +class _OngoingAnimationState extends State<OngoingAnimationByModifyingEndTweenValue> { + double _newValue = .4; + Color _newColor = Colors.white; + + @override + Widget build(BuildContext context) { + return Stack( + children: <Widget>[ + starsBackground, + Column( + children: <Widget>[ + Center( + child: TweenAnimationBuilder( + tween: ColorTween(begin: Colors.white, end: _newColor), + duration: Duration(seconds: 2), + builder: (_, Color color, __) { + return ColorFiltered( + child: Image.asset('assets/sun.png'), + colorFilter: ColorFilter.mode(color, BlendMode.modulate), + ); + }, + ), + ), + Slider.adaptive( + value: _newValue, + onChanged: (double value) { + setState(() { + _newValue = value; + _newColor = Color.lerp(Colors.white, Colors.red, value); + }); + }, + ), + ], + ), + ], + ); + } +} +``` + +I changed the code to also include a `Slider` widget. Then I declared a local variable called `_newColor` that takes the slider value and converts it to a color. `_newColor` is also used as the end value in my `Tween`. Now the animation updates every time I drag the slider. + +<DashImage figure src="images/1ZiMTAgg11y3BHH24bTt6Mg.gif" /> + + +One thing to keep in mind is that `TweenAnimationBuilder` always moves from the current value to the new end value. That means as I drag the slider, I see the color change relative to its previous color, rather than always animating from white at the very beginning. Just by setting a new end value to my `Tween`, I can reverse my animation or move to any point in between. `TweenAnimationBuilder` always smoothly animates between its current value and the new end point. As you can perhaps infer, this means dynamically changing the *start* of your `Tween` has no effect. + +```dart +// DON'T DO THIS! YOU WON'T SEE AN ANIMATION IF YOU JUST UPDATE THE START VALUE! +class NopeNopeNope extends StatefulWidget { + @override + _NopeNopeNopeState createState() => _NopeNopeNopeState(); +} + +class _NopeNopeNopeState extends State<NopeNopeNope> { + double _newValue = .4; + Color _newColor = Colors.white; + + @override + Widget build(BuildContext context) { + return Stack( + children: <Widget>[ + starsBackground, + Column( + children: <Widget>[ + Center( + child: TweenAnimationBuilder( + tween: ColorTween(begin: _newColor, end: Colors.red), + duration: Duration(seconds: 2), + builder: (_, Color color, __) { + return ColorFiltered( + child: Image.asset('assets/sun.png'), + colorFilter: ColorFilter.mode(color, BlendMode.modulate), + ); + }, + ), + ), + Slider.adaptive( + value: _newValue, + onChanged: (double value) { + setState(() { + _newValue = value; + _newColor = Color.lerp(Colors.white, Colors.red, value); + }); + }, + ), + ], + ), + ], + ); + } +} +``` + +## onEnd and child + +There are a few other parameters that I haven’t talked about yet. The first one is the curve, to describe how we should transition between the beginning and end values in our `Tween` range. In the [previous article](https://medium.com/flutter/flutter-animation-basics-with-implicit-animations-95db481c5916) we talked about how you can even create a custom curve, but there are a lot of great predefined options too. + +The second one is a callback you can specify, so you can do something when the animation completes. Perhaps you want to make another widget appear after this animation finishes. You can also use this callback as a way to reverse your animation back and forth. I recommend that you think carefully before doing this though. The callback makes the type of animation you’re trying to do less clear because the value setting is distributed through your code. Because the values are discontinuous (jumping back to the start again), if you want a repeating animation you’ll need some sort of explicit animation: either a built-in explicit animated widget or extending `AnimatedWidget`. + +```dart +class BackAndForth extends StatefulWidget { + @override + _BackAndForthState createState() => _BackAndForthState(); +} + +class _BackAndForthState extends State<MyHomePage> { + Color _newColor = Colors.red; + + @override + Widget build(BuildContext context) { + return Stack( + children: <Widget>[ + starsBackground, + Center( + child: TweenAnimationBuilder( + tween: ColorTween(begin: Colors.white, end: _newColor), + duration: Duration(seconds: 2), + onEnd: () { + setState(() { + _newColor = _newColor == Colors.red ? Colors.white : Colors.red; + }); + }, + builder: (_, Color color, __) { + return ColorFiltered( + child: Image.asset('assets/sun.png'), + colorFilter: ColorFilter.mode(color, BlendMode.modulate), + ); + }, + ), + ) + ], + ); + } +} +``` + +<DashImage figure src="images/17xwzsHM8FSwtXlWP_3q9qA.webp" /> + + +There’s one last parameter we haven’t discussed yet: the child parameter. Setting the child parameter is a potential performance optimization. Even though the color changes, the star image widget itself stays the same. As it’s currently written though, that image widget gets reconstructed every time that builder method gets called. We can build that star image ahead of time by passing it in as a child parameter. This way, Flutter knows the only widget that it needs to rebuild from frame to frame is the new color filter, not the star image itself. This example is simple so there’s really no noticeable difference. But, if we were animating a much more complex component, you can imagine performance optimization might become more important. + +```dart +class ChildParameter extends StatelessWidget { + static final colorTween = ColorTween(begin: Colors.white, end: Colors.red); + + @override + Widget build(BuildContext context) { + return Stack( + children: <Widget>[ + starsBackground, + Center( + child: TweenAnimationBuilder<Color>( + tween: colorTween, + child: Image.asset('assets/sun.png'), + duration: Duration(seconds: 2), + builder: (_, Color color, Widget myChild) { + return ColorFiltered( + child: myChild, + colorFilter: ColorFilter.mode(color, BlendMode.modulate), + ); + }, + ), + ), + ], + ); + } +} +``` + +## Summary + +That’s all you need to know to write your own cool implicit animations with `TweenAnimationBuilder`! To recap, `TweenAnimationBuilder` is a great way to create “set-it-and-forget-it” implicit animations if you can’t find a built-in `AnimatedFoo` type widget. You can accomplish simple animations using `TweenAnimationBuilder` without needing to use a `StatefulWidget`. You can change that end value in the `Tween` to smoothly animate to a new value. There are also potential performance optimizations by passing in a child ahead of time or setting a static final `Tween` when appropriate. To learn more about which animation widget you should use when, check out the other articles in this series. + +This is the article version of the following video content. If you prefer videos, watch away: + +<YoutubeEmbed id="6KiPEqzJIKQ" title="Creating your own Custom Implicit Animations with TweenAnimationBuilder" fullwidth="true"/> + + +Articles in this series: + +* [How to Choose Which Flutter Animation Widget is Right for You?](https://medium.com/p/79ecfb7e72b5/edit) + +* [Flutter animation basics with implicit animations](https://medium.com/flutter/flutter-animation-basics-with-implicit-animations-95db481c5916) + +* [Custom Implicit Animations in Flutter…with TweenAnimationBuilder](https://medium.com/flutter/custom-implicit-animations-in-flutter-with-tweenanimationbuilder-c76540b47185) (this article) + +* [Directional animations with built-in explicit animations](https://medium.com/flutter/directional-animations-with-built-in-explicit-animations-3e7c5e6fbbd7) + +* [When should I useAnimatedBuilder or AnimatedWidget?](https://medium.com/flutter/when-should-i-useanimatedbuilder-or-animatedwidget-57ecae0959e8) + +* [Animation deep dive](https://medium.com/flutter/animation-deep-dive-39d3ffea111f) \ No newline at end of file diff --git a/sites/www/content/blog/dart-flutter-devtools-extensions/images/0s9-Y9X5dTyuk6Xj4.webp b/sites/www/content/blog/dart-flutter-devtools-extensions/images/0s9-Y9X5dTyuk6Xj4.webp new file mode 100644 index 0000000000..de53529856 Binary files /dev/null and b/sites/www/content/blog/dart-flutter-devtools-extensions/images/0s9-Y9X5dTyuk6Xj4.webp differ diff --git a/sites/www/content/blog/dart-flutter-devtools-extensions/images/1-3c-p57ai0Quc5rJOL9qEQ.webp b/sites/www/content/blog/dart-flutter-devtools-extensions/images/1-3c-p57ai0Quc5rJOL9qEQ.webp new file mode 100644 index 0000000000..36079b2367 Binary files /dev/null and b/sites/www/content/blog/dart-flutter-devtools-extensions/images/1-3c-p57ai0Quc5rJOL9qEQ.webp differ diff --git a/sites/www/content/blog/dart-flutter-devtools-extensions/images/1DSi_p-2FO60qo5JUKXk-3Q.webp b/sites/www/content/blog/dart-flutter-devtools-extensions/images/1DSi_p-2FO60qo5JUKXk-3Q.webp new file mode 100644 index 0000000000..42ac582db8 Binary files /dev/null and b/sites/www/content/blog/dart-flutter-devtools-extensions/images/1DSi_p-2FO60qo5JUKXk-3Q.webp differ diff --git a/sites/www/content/blog/dart-flutter-devtools-extensions/images/1FFl2jeYpyn8DfjRI5xQ8mQ.webp b/sites/www/content/blog/dart-flutter-devtools-extensions/images/1FFl2jeYpyn8DfjRI5xQ8mQ.webp new file mode 100644 index 0000000000..064449cdaf Binary files /dev/null and b/sites/www/content/blog/dart-flutter-devtools-extensions/images/1FFl2jeYpyn8DfjRI5xQ8mQ.webp differ diff --git a/sites/www/content/blog/dart-flutter-devtools-extensions/images/1H0Q1Zv2L6_NUFUedI6mkQw.webp b/sites/www/content/blog/dart-flutter-devtools-extensions/images/1H0Q1Zv2L6_NUFUedI6mkQw.webp new file mode 100644 index 0000000000..9b6dc1e5eb Binary files /dev/null and b/sites/www/content/blog/dart-flutter-devtools-extensions/images/1H0Q1Zv2L6_NUFUedI6mkQw.webp differ diff --git a/sites/www/content/blog/dart-flutter-devtools-extensions/images/1JsdgKjxlVmm5EAFfXvQ2yA.webp b/sites/www/content/blog/dart-flutter-devtools-extensions/images/1JsdgKjxlVmm5EAFfXvQ2yA.webp new file mode 100644 index 0000000000..705b138ee2 Binary files /dev/null and b/sites/www/content/blog/dart-flutter-devtools-extensions/images/1JsdgKjxlVmm5EAFfXvQ2yA.webp differ diff --git a/sites/www/content/blog/dart-flutter-devtools-extensions/images/1bkNzahdp7eXUo7Xwh1U07Q.webp b/sites/www/content/blog/dart-flutter-devtools-extensions/images/1bkNzahdp7eXUo7Xwh1U07Q.webp new file mode 100644 index 0000000000..06641103ed Binary files /dev/null and b/sites/www/content/blog/dart-flutter-devtools-extensions/images/1bkNzahdp7eXUo7Xwh1U07Q.webp differ diff --git a/sites/www/content/blog/dart-flutter-devtools-extensions/index.md b/sites/www/content/blog/dart-flutter-devtools-extensions/index.md new file mode 100644 index 0000000000..4fef0e9984 --- /dev/null +++ b/sites/www/content/blog/dart-flutter-devtools-extensions/index.md @@ -0,0 +1,357 @@ +--- +title: "Dart & Flutter DevTools Extensions" +description: "A guide for building custom tooling in Dart & Flutter DevTools" +publishDate: 2023-11-15 +author: kenzieschmoll +image: images/1JsdgKjxlVmm5EAFfXvQ2yA.webp +category: announcements +layout: blog +--- + +Have you ever wanted to build developer tooling for Dart and Flutter but didn’t know where to start? Or maybe you didn’t want to go through all the work of establishing a connection to a running Dart or Flutter application to access debugging data? Then, even if you did create a development tool, how would you deploy it or give users easy access to it? Well, we have some good news for you: now you can create developer tooling without all these hurdles. + +With the new Dart & Flutter DevTools extensions framework, you can easily build developer tooling that is tightly integrated with the existing DevTools tooling suite. Extensions are built using Flutter web and leverage existing frameworks and utilities from DevTools to simplify the developer tool authoring experience. + +<DashImage figure src="images/1JsdgKjxlVmm5EAFfXvQ2yA.webp" alt="Example DevTools extension for package:foo" caption="Example DevTools extension for package:foo" /> + + +## How do DevTools extensions work? + +Extensions are shipped as part of a pub package. You can add a DevTools extension to an existing pub package, or you can create a new package that provides a DevTools extension only. In both these scenarios, the end-user must list a dependency on the package providing the DevTools extension in order to see the extension in DevTools. + +For example, imagine we have some `package:foo`, and this package provides a DevTools extension. When a user depends on `package:foo` in their app, they automatically get access to the DevTools extension provided by this package. When DevTools detects the `package:foo`extension is available, based on information from the user’s app or from their IDE, a new tab “Foo” will be added to DevTools that contains the developer tools provided by `package:foo`. + +<DashImage figure src="images/1FFl2jeYpyn8DfjRI5xQ8mQ.webp" /> + + +Some examples of packages that have added a DevTools extension to an existing package are [package:provider](https://pub.dev/packages/provider), [package:patrol](https://pub.dev/packages/patrol), and [package:drift](https://pub.dev/packages/drift). + +## What types of tools are supported? + +With the DevTools extensions framework you can build many types of tools, including: + +* Companion tools for existing packages + +* New tools that are shipped as their own package + +* Tools that interact with a running application + +* Tools that **do not** interact with a running application + +* Tools that interact with project files opened in the IDE + +* *Planned for the future: tools that interact with the Analysis server (upvote this [issue](https://github.com/flutter/devtools/issues/7658) if you want this feature sooner!)* + +The DevTools Extensions framework comes with out-of-the-box features that make distributing your extension to users seamless: + +* Users can use your tool from DevTools in the browser + +* Users can use your tool embedded directly in their IDE + +* Users can discover and open your tool from Dart & Flutter supported IDEs + +Now let’s get started! + +## Writing a DevTools extension: a step-by-step guide + +### Before you get started + +What you will need: + +* Flutter SDK >= `3.17.0–0.0.pre` & Dart SDK >= `3.2`. + +* A [Pub](https://pub.dev/) package (existing or new) to add a DevTools extension to. +> To use the latest [devtools_extensions](https://pub.dev/packages/devtools_extensions) and [devtools_app_shared](https://pub.dev/packages/devtools_app_shared) packages, it is acceptable to develop your extension from the Flutter master or beta channel. + +## Step 1: Set up your package hierarchy + +### Standalone extensions + +For a standalone extension (an extension that is not being shipped as part of an existing pub package), it is acceptable to include your extension source code in the same package that the extension is shipped with. This will simplify development, and since users of your package will add a dependency on your package as a `dev_dependency`, the size of your package will not affect the user's app size. Your package structure will look like this: + +```bash +my_new_tool + extension/ + devtools/ + build/ + ... # pre-compiled output of the Flutter web app under lib/ + config.yaml + lib/ # source code for your extension Flutter web app + src/ + ... +``` + + +Since the extension must be built as a Flutter web app, use `flutter create` to generate the package for your DevTools extension: + +```bash +flutter create --template app --platforms web my_new_tool +``` + + +Now, use the `my_new_tool` package to configure your extension in the next step. + +### Companion extensions + +For a companion extension (an extension that is shipped as part of an existing pub package), we recommend that you place your extension source code outside of your pub package. This will help keep your package size as small as possible, since you will want to avoid inflating the size of user apps that depend on your package. Here is the recommended package structure: + +```bash +foo/ # formerly the repository root of your pub package + packages/ + foo/ # your pub package + extension/ + devtools/ + build/ + ... # pre-compiled output of foo_devtools_extension/lib + config.yaml + foo_devtools_extension/ + lib/ # source code for your extension Flutter web app +``` + + +## Step 2: Configure your extension + +In the Dart package that will provide the DevTools extension to users, add a top-level `extension` directory: + +``` +foo/ + extension/ + lib/ + ... +``` + + +Under the `extension` directory, create the following structure **exactly as shown**: + +``` +extension/ + devtools/ + build/ + config.yaml +``` + + +The `config.yaml` file contains metadata that DevTools needs to load the extension: + +```yaml +name: foo +version: 0.0.1 +issueTracker: <link_to_your_issue_tracker.com> +materialIconCodePoint: '0xe0b1' +requiresConnection: true # optional field - defaults to true +``` + + +Copy the `config.yaml` file content above and paste it into the `config.yaml` file you just created in your package. **It is important that you use the exact file name and field names as shown, or else your extension may fail to load in DevTools.** + +For each key, fill in the appropriate value for your package. + +* `name`: the package name that this DevTools extension belongs to. The value of this field is used in the extension page title bar. **[required]** + +* `version`: the version of your DevTools extension. This version number should evolve over time as you ship new features for your extension. The value of this field is used in the extension page title bar. **[required]** + +* `issueTracker`: the URL for your issue tracker. When a user clicks the “Report an issue” link in the DevTools UI, they are directed to this URL. **[required]** + +<DashImage figure src="images/1H0Q1Zv2L6_NUFUedI6mkQw.webp" alt="DevTools extension screen title bar" caption="DevTools extension screen title bar" /> + + +* `materialIconCodePoint`: corresponds to the codepoint value of an icon from [material/icons.dart](https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/material/icons.dart). This icon is used for the extension’s tab in the top-level DevTools tab bar. **[required]** + +<DashImage figure src="images/1bkNzahdp7eXUo7Xwh1U07Q.webp" alt="DevTools extension tab icon" caption="DevTools extension tab icon" /> + + +* `requiresConnection`: whether the extension requires a connected Dart or Flutter app to use. This is an optional field that will default to `true` if unspecified. **[optional]** + +For the most up-to-date documentation on the `config.yaml` spec, see [extension_config_spec.md](https://github.com/flutter/devtools/blob/master/packages/devtools_extensions/extension_config_spec.md). + +Now it is time to build your extension. + +## Step 3: Build your extension + +1. Create the Flutter web app. + +*Note: skip this step if you are building a standalone extension, since you already did this when you set up your package hierarchy.* + +From the directory where you want your extension source code to live, run the following command, replacing `foo_devtools_extension` with `<your_package_name>_devtools_extension`: + +```shell +flutter create --template app --platforms web foo_devtools_extension +``` + + +2. Add the `package:devtools_extensions` dependency to your extension Flutter web app. + +```shell +flutter pub add devtools_extensions +``` + + +You will likely also want to add a dependency on [package:devtools_app_shared](https://pub.dev/packages/devtools_app_shared), which contains shared services, utilities, and UI components for you to use while building your extension. See [devtools_app_shared/example](https://github.com/flutter/devtools/tree/master/packages/devtools_app_shared/example) for sample usages. + +```shell +flutter pub add devtools_app_shared +``` + + +3. Add the `DevToolsExtension` widget at the root of your Fluter web app. + +In `lib/main.dart`, add the following: + +```dart +import 'package:devtools_extensions/devtools_extensions.dart'; +import 'package:flutter/material.dart'; + +void main() { + runApp(const DevToolsExtension()); +} + +class FooDevToolsExtension extends StatelessWidget { + const FooDevToolsExtension({super.key}); + + @override + Widget build(BuildContext context) { + return const DevToolsExtension( + child: Placeholder(), // Build your extension here + ); + } +} +``` + + +The `DevToolsExtension` widget automatically performs all extension initialization required to interact with DevTools. From anywhere in your extension web app, you can access the globals: + +* `extensionManager`: a manager for interacting with DevTools or the extensions framework. + +* `serviceManager`: a manager for interacting with the connected vm service, if present. + +* `dtdManager`: a manager for interacting with the Dart Tooling Daemon, if present. + +## Step 4: Debug your extension + +When developing and maintaining your DevTools extension, you’ll want to run, debug, and test your extension Flutter web app. You have a couple of different options for this, outlined below. + +### Option A: Use the Simulated DevTools Environment (recommended for development) + +For debugging purposes, you will likely want to use the “simulated DevTools environment”. This is a simulated environment that allows you to build your extension without having to develop it as an embedded iFrame in DevTools. Running your extension this way will wrap your extension with an environment that simulates the DevTools-to-DevTools extension connection. It also gives you access to hot restart and a faster development cycle. + +<DashImage figure src="images/1-3c-p57ai0Quc5rJOL9qEQ.webp" alt="Debugging an extension with the Simulated DevTools Environment" caption="Debugging an extension with the Simulated DevTools Environment" /> + + +1. *Your DevTools extension.* + +1. *The VM service URI for a test app that your DevTools extension will interact with. This app should depend on your extension’s parent package (package:foo in this example).* + +1. *Buttons to perform actions that a user may trigger from DevTools.* + +1. *Logs showing the messages that will be sent between your extension and DevTools.* + +The simulated environment is enabled by the environment parameter `use_simulated_environment`. To run your extension web app with this flag enabled, add a configuration to your `launch.json` file in VS code: + +```json +{ + ... + "configurations": [ + ... + { + "name": "foo_devtools_extension + simulated environment", + "cwd": "packages/foo_devtools_extension", + "request": "launch", + "type": "dart", + "args": [ + "--dart-define=use_simulated_environment=true" + ], + }, + ] +} +``` + + +or launch your app from the command line with the added flag: + +```shell +flutter run -d chrome - dart-define=use_simulated_environment=true +``` + + +### Option B: Use a real DevTools environment + +Once you develop your extension to a point where you are ready to test your changes in a real DevTools environment, you need to perform a series of setup steps: + +1. Develop your extension to a point where you are ready to test your changes in a real DevTools environment. Build your flutter web app and copy the built assets from `your_extension_web_app/build/web` to your pub package's `extension/devtools/build` directory. + +Use the `build_and_copy` command from `package:devtools_extensions` to help with this step. + +```shell +cd your_extension_web_app; +flutter pub get; +dart run devtools_extensions build_and_copy --source=. --dest=path/to/your_pub_package/extension/devtools +``` + +> Note: if you are using the recommended package structure from above for adding an extension to an existing pub package, the value for *`— dest`* should be *`../your_pub_package/extension/devtools`*. + +To ensure that your extension is setup properly for loading in DevTools, run the `validate` command from `package:devtools_extensions`. The `--package` argument should point to the root of the Dart package that this extension will be published with. + +```shell +cd your_extension_web_app; +flutter pub get; +dart run devtools_extensions validate --package=path/to/your_pub_package +``` + + +2. Prepare and a test environment with a dependency on your pub package that is providing the extension. + +In the Dart or Flutter project where you are adding a dependency on your package, add a [path](https://dart.dev/tools/pub/dependencies#path-packages) dependency that points to your local package source code (the package that contains the `extension/devtools/` directory with your extension's assets). Once you have done this, run `pub get` on the package. + +* **If your extension requires a running application**, then you’ll need to run the app that depends on your extension. + +* **If your extension does not require a running application**, then you will need to open the test Dart or Flutter project that depends on your package in a supported IDE (VS Code or IntelliJ / Android Studio). + +3. Start DevTools in one of the following ways: + +* **If your extension requires a running application**, you can open DevTools either from the URI that was printed to command line when you ran the test app, or from the IDE where you ran your test app. + +* **If your extension does not require a running application**, you can open your Dart or Flutter project that depends on your package in a supported IDE (VS Code or IntelliJ / Android Studio). Open DevTools from the IDE to see your extension in the browser. + +* **If you need local or unreleased changes from DevTools**, you’ll need to build and run DevTools from source. See the DevTools [CONTRIBUTING.md](https://github.com/flutter/devtools/blob/7a53fd2ddf289ee1b76840b9bfd9a2cb7876f13b/packages/devtools_extensions) for a guide on how to do this. You’ll need to build DevTools with the server and the front end to test extensions — see [instructions](https://github.com/flutter/devtools/blob/master/CONTRIBUTING.md#development-devtools-server--devtools-flutter-web-app). + +4. Once you have opened DevTools, you should see a tab in the DevTools app bar for your extension. The enabled or disabled state of your extension is managed by DevTools, which is exposed from an “Extensions” menu, available from the action buttons in the upper right corner of the screen. + +<DashImage figure src="images/0s9-Y9X5dTyuk6Xj4.webp" alt="DevTools Extensions menu button" caption="DevTools Extensions menu button" /> + + +<DashImage figure src="images/1DSi_p-2FO60qo5JUKXk-3Q.webp" alt="DevTools Extensions menu" caption="DevTools Extensions menu" /> + + +## Step 5: Publish your package with a DevTools extension + +In order for a package to provide a DevTools extension to its users, it must be published with the expected content in the `your_pub_package/extension/devtools/` directory (see the setup instructions above). + +1. Ensure the `extension/devtools/config.yaml` file exists and is configured per the specifications above. You can run the `validate` command from `package:devtools_extensions` to verify. + +```bash +cd your_extension_web_app; +flutter pub get; +dart run devtools_extensions validate --package=path/to/pkg_providing_your_extension_assets +``` + + +2. Use the `build_and_copy` command provided by `package:devtools_extensions` to build your extension and copy the output to the `extension/devtools` directory: + +```shell +cd your_extension_web_app; +flutter pub get; +dart run devtools_extensions build_and_copy --source=. --dest=path/to/your_pub_package/extension/devtools +``` + + +Then publish your package on [pub.dev](https://pub.dev/): `flutter pub publish`. When running `pub publish`, you will see a warning if you do not have the `config.yaml` file and a non-empty `build` directory as required. + +For additional guidance around publishing your package, see the `package:devtools_extensions` [publishing guide](https://pub.dev/packages/devtools_extensions#publish-your-package-with-a-devtools-extension). + +## Conclusion + +That’s it! Now, when a user depends on the latest version of your package, they will automatically get access to the tools you provide in your DevTools extension. + +For issues and feature requests, please file an issue on the DevTools [issue tracker](https://github.com/flutter/devtools/issues). + +For general support and access to the community of DevTools extension authors, check out the [#devtools-extension-authors](https://discord.com/channels/608014603317936148/1159561514072690739) Discord channel (you will first need to join the [Flutter Discord server](https://github.com/flutter/flutter/wiki/Chat)). \ No newline at end of file diff --git a/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/images/0BkRheYyGtlw1zwLZ.webp b/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/images/0BkRheYyGtlw1zwLZ.webp new file mode 100644 index 0000000000..77ebdaa816 Binary files /dev/null and b/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/images/0BkRheYyGtlw1zwLZ.webp differ diff --git a/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/images/0DyOOI5cfR4759Ka2.webp b/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/images/0DyOOI5cfR4759Ka2.webp new file mode 100644 index 0000000000..c690a8c580 Binary files /dev/null and b/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/images/0DyOOI5cfR4759Ka2.webp differ diff --git a/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/images/0XtA6olQqCpKPyrdO.webp b/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/images/0XtA6olQqCpKPyrdO.webp new file mode 100644 index 0000000000..bd2a6ff7f2 Binary files /dev/null and b/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/images/0XtA6olQqCpKPyrdO.webp differ diff --git a/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/images/0XzkfOCEYvTEHsSyh.gif b/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/images/0XzkfOCEYvTEHsSyh.gif new file mode 100644 index 0000000000..302d5eee16 Binary files /dev/null and b/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/images/0XzkfOCEYvTEHsSyh.gif differ diff --git a/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/images/0n4GWT45AkStIiLZy.webp b/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/images/0n4GWT45AkStIiLZy.webp new file mode 100644 index 0000000000..0c1c67da2e Binary files /dev/null and b/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/images/0n4GWT45AkStIiLZy.webp differ diff --git a/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/images/1bvMcF-Tc30i2dMr2jFfECA.webp b/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/images/1bvMcF-Tc30i2dMr2jFfECA.webp new file mode 100644 index 0000000000..bc1b2139a1 Binary files /dev/null and b/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/images/1bvMcF-Tc30i2dMr2jFfECA.webp differ diff --git a/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/index.md b/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/index.md new file mode 100644 index 0000000000..718a8ce812 --- /dev/null +++ b/sites/www/content/blog/dart-flutter-momentum-at-google-i-o-2025/index.md @@ -0,0 +1,218 @@ +--- +title: "Dart & Flutter momentum at Google I/O 2025" +description: "Today at the Google I/O conference, hosted at Shoreline Amphitheatre in Mountain View, California, we shared how Flutter continues to gain…" +publishDate: 2025-05-21 +author: mit-mit +image: images/0XtA6olQqCpKPyrdO.webp +category: events +layout: blog +--- + +Today at the Google I/O conference, hosted at Shoreline Amphitheatre in Mountain View, California, we shared how Flutter continues to gain momentum as a production-grade app framework trusted by some of the world’s largest brands. + +<YoutubeEmbed id="v6Rzo5khNE8" title="What's new in Flutter" fullwidth="true"/> + + +First, according to the JetBrains [State of the Developer Ecosystem](https://www.jetbrains.com/lp/devecosystem-2024/) survey, Flutter has been the most used multi-platform app framework, since 2021. + +Second, according to app intelligence provider AppTopia, Flutter accounts for nearly 30% of all new free iOS apps. +> *“Apptopia tracks millions of apps in the Apple AppStore and Google Play Store, and analyzes and detects which developer SDKs were used to create the apps. Flutter is one of the most popular SDKs we track: In the Apple AppStore it has grown steadily in usage from around 10% of all tracked free apps in 2021 to nearly 30% of all tracked free apps in 2024!”* +> — Apptopia Inc. + +## Customer updates + +Here are a few of the brands and apps that exemplify this growth, and the unique power of Flutter and its ability to target any platform from a single, shared source code base. + +* **NotebookLM**: A new AI-powered research and writing assistant, made by Google Labs, just launched their mobile apps built with Flutter. Previously, this was only available as a website, but the team received many requests for mobile apps. With development speed and time-to-market being critical, Flutter was a natural choice. We’re excited about its [public release](https://blog.google/technology/ai/notebooklm-app/) this week. + +* **Google Cloud**: The Google Cloud team took their existing iOS/Android apps, and switched to Flutter for building a range of new features like their new Cloud Assist, with the goal of writing once and deploying everywhere. They found tremendous value in the UI and logic consistency they get from having a single, shared codebase and felt Flutter offers native-like performance and feel without compromises. + +* **Universal**: We announced last year how Universal Destinations & Experiences uses Flutter for their [iOS and Android Apps at their parks](https://flutter.dev/showcase/universal-studios) in Orlando, Hollywood, and Osaka. At their newest theme park, Universal Epic Universe in Orlando, they are now also using Flutter to power their guest-facing self-service dining kiosks. They were able to take their app, and reimagine it as a kiosk interface — with rich animations and smooth transitions. This is a great showcase of just how flexible and powerful Flutter really is. + +* **Reflection.app**: Powered by [a complete Google stack](https://www.reflection.app/blog/google-gemini-vertex-ai-firebase-journaling-app) — Flutter, Firebase, and Gemini — to deliver a thoughtful journaling experience to their users. + +* **teamLab**: Based in Tokyo, [teamLab](https://youtu.be/OyS812ZoTzw) creates solutions for digital content production, urban planning, and architectural space design. Their museum apps allow visitors to read more about the exhibits, and even interact digitally with the pieces on the phone. So far they have seen 35 million users of this new experience. + +* **1080 Motion:** Another example is 1080 Motion, out of Sweden, who offer state-of-the-art training and rehabilitation for professional athletes around the world. Their customers receive real time performance feedback on hardware that delivers individualized workouts tailored for that athlete, on that day. + +* **GE Appliances:** GE Appliances is using Flutter to revolutionize the UIs on small screens found on modern appliances equipped with an LCD screen. Their team, comprising many embedded and JavaScript developers, found Dart easy to learn, which accelerated adoption and allowed them to become productive quickly. In the future, they plan to expand on its embedded starting point by building user-facing mobile apps with Dart and Flutter. + +## Evolving the Dart language + +Dart is a key ingredient to Flutter’s success. Our ability to evolve Dart and Flutter together is a core enabler of Flutter’s industry leading developer experience and production quality. + +We continue to evolve the Dart language — here’s a visualization of all the improvements we’ve made, since our major null safety feature, back in 2021. + +<DashImage figure src="images/0XtA6olQqCpKPyrdO.webp" alt="Dart language evolution" caption="Dart language evolution" /> + + +## Null-aware elements + +Today [in Dart 3.8](https://medium.com/dartlang/announcing-dart-3-8-724eaaec9f47), we’re adding **null-aware elements**, which allows you to include an element in a collection *only* if its runtime value is non-null. You can use this feature to rewrite code like this: + +```dart +// Code without null-aware elements. +var listWithoutNullAwareElements = [ +if (promotableNullableValue != null) + promotableNullableValue, +if (nullable.value != null) + nullable.value!, +if (nullable.value case var value?) + value, +]; + +With null-aware elements, remove all those if statements: +// Code with null-aware elements. +var listWithNullAwareElements = [ + ?promotableNullableValue, + ?nullable.value, + ?nullable.value, +]; +``` + + +## Dot shorthands + +We also shared the news that we’re working on a new language feature called [**dot shorthands**](https://github.com/dart-lang/language/issues/357) — projected to release later this year — for addressing duplication in Flutter code with a new shorter syntax for selecting a member on a type that is known based on the context: + +```dart +// Code without dot shorthands. +Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: […], +); + +// Code with dot shorthands. +Column( + crossAxisAlignment: .stretch, + mainAxisSize: .min, + children: […], +); +``` + + +## Updating the developer experience + +We take the Dart and Flutter developer experience very seriously and have several updates. + +### Property editor + +One of the trickiest parts of any framework is learning what all the available APIs are. This can be overwhelming at first. Great developer tooling can really help here — today we’re announcing a new property editor feature, available today in Flutter 3.32. + +<DashImage figure src="images/0XzkfOCEYvTEHsSyh.gif" alt="Property editor in VS Code" caption="Property editor in VS Code" /> + + +When you select a widget in your IDE, it shows the primary layout properties without having to read docs or go to declaration. You can then easily change them directly in the property editor, and have those changes propagate to the source code. + +### Pub.dev improvements + +We’ve also made several improvements to [pub.dev](https://pub.dev), the Dart package manager: We added dark mode support for better contrast, a new download counts feature that helps you understand how much a package is used, and how that use is distributed over the available versions. + +A brand new feature we’re announcing this week is Trending Packages, which shows packages that have a significant recent uptick in usage. This can be used for discovering new packages that might be of interest. + +<DashImage figure src="images/0BkRheYyGtlw1zwLZ.webp" alt="pub.dev trending packages" caption="pub.dev trending packages" /> + + +### Improved formatting + +The Dart formatter is used by most Dart developers. Earlier this year, we added new functionality to make it automatically manage trailing commas. Based on your feedback, we made a number of adjustments in this week’s Dart 3.8 release, and added a new configuration option to tell the formatter to never remove trailing commas if you prefer to manually maintain these. + +### Dart cross-compilation + +Next, we’ve also improved the Dart compiler with the ability to compile Dart executables for Linux from Windows or macOS. This is particularly useful when targeting smaller embedded devices, like a Raspberry Pi. Before this, if you didn’t have a Linux machine, you had to compile Dart on the Raspberry Pi itself. + +## Integrating more deeply with platforms + +### Direct native interoperability + +Last year we announced a new initiative — direct native interop — with a vision of eventually allowing you to access native APIs as easily as you access your own Dart code. Today we shared several updates: + +* **Thread merge**: Historically, the Flutter engine has used two separate threads — the platform thread and the UI thread. The vast majority of platform APIs must be accessed on the platform thread, but the Dart code was running on the UI thread, making calling platform API directly from Dart cumbersome. This work is now completed in stable for Android & iOS, and in Flutter 3.33 beta for Windows and macOS. Work to merge threads on Linux is underway. For details see the [tracking issue](https://github.com/flutter/flutter/issues/150525). + +* **Native assets/build hooks**: Sometimes you need to talk to third party libraries written in another language — for example, a machine learning library written in C. [Build hooks](https://github.com/dart-lang/sdk/issues/50565) (formerly known as native assets) support bundling native code with Dart packages, and as of today, is available in preview in the main channel. + +* **ffigen/jnigen**: We continue to improve the ffigen & jnigen wrapper generators, and hope to share more news about these in the second half of the year. + +### Great on Android + +* **Impeller**: As of 3.29, Android has joined iOS in using Impeller as its default renderer, with the exception being a small set of nearly 10 year old Android devices which have a very old graphics stack. To support these devices, we will continue to use Skia on Android API-level 28 and lower. + +* **Edge-to-edge**: As of Flutter 3.27, we made Android’s edge-to-edge UI system Flutter’s default. This support opens up the entire height of the display to Flutter apps running on devices that have no system bars at the top or bottom of the display. This had delicate implications on how Flutter thinks about things like SafeAreas and system UI. + +* **Scribe**: This year we also added support for Android’s “Scribe” feature, which converts raw handwriting into actual text. + +* **Material updates**: We’ve delivered numerous updates to Material, including supporting new page transitions, updated progress indicators and sliders, and other areas of polish. + +### Great on iOS + +We want developers building for iOS to be able to deliver beautiful, feature-rich apps, so this year we’ve been hard at work improving fidelity for iOS and improving Flutter’s integration with the Apple developer ecosystem: + +* **Cupertino update**: A large investment in the Cupertino library has been made to improve its fidelity and expand its coverage. We revisited spring and scrolling physics, landed numerous updates to widgets such as our bottom sheets, collapsible widgets, navigation bars, (especially those with search fields), and more. + +* **Swift Package Manager**: We are migrating from Cocoapods to Swift Package Manager to simplify our dev setups. This transition is ongoing, but over 150 plugins have added Swift Package Manager support and we’re happy to announce that that includes all Firebase plugins. We aim to complete the transition to Swift Package Manager soon! + +* **Squircles**: And, one more thing… We’re added new support for one of Apple’s unique shapes, known as a *squircle*, which blends the arcs of several different shapes! The precise algorithm to the iOS squircle remains a mystery to many, but that hasn’t stopped us from creating our take on it, to the highest possible fidelity. You can find this in the stable channel today as the [RSuperellipse](https://api.flutter.dev/flutter/dart-ui/RSuperellipse-class.html) class and we’re already including it in our Cupertino widgets where appropriate. + +### Great on web + +* **WebAssembly**: Last year we announced support for compiling Flutter web apps WasmGC — to make apps start faster, and render frames up to twice as fast in complicated scenarios. + +* **Accessibility**: We’ve done a lot of work on our accessibility system, both in terms of performance, and have added an entire new Semantics Roles system that Flutter converts to standard aria labels on the web. + +* **Stateful hot reload**: If you’ve built Flutter apps for mobile and wanted to take those experiences to the web, you’ve probably felt the absence of Flutter’s flagship feature — stateful hot reload — when working on Flutter web apps. Today, we announced that stateful hot reload for Flutter web is available in preview in all channels. One place you can experience it right away is in [DartPad](https://dartpad.dev). + +## Expanding the ecosystem + +Google is proud to support Flutter, but so much of the value for Flutter developers comes from our larger community. + +### Canonical + +One big contributor is Canonical, the folks behind Ubuntu. They use Flutter themselves for Ubuntu’s first-party app marketplace, and have been excited to contribute toward Flutter’s overall quality on desktop, including contributing feature work for multi-window support. + +<DashImage figure src="images/0DyOOI5cfR4759Ka2.webp" alt="Multi-window support running on Linux" caption="Multi-window support running on Linux" /> + + +They have also contributed to the desktop implementation of Flutter’s significant [thread merge](https://github.com/flutter/flutter/issues/150525). And they have fixed many issues related to accessibility, text input, focus, and keyboard & mouse events. + +### LG + +Another company with a business-critical interest in Flutter is LG. Last year, LG announced plans to rewrite many of their core webOS system apps in Flutter. This year, LG is excited to announce a significant expansion of their investment in Flutter, aiming to complete the development of a new webOS-Flutter SDK, which will allow developers to release Flutter apps to the LG Content Store. They are targeting an official launch in the first half of next year. + +## Embracing AI + +We have lots of news in the exciting and fast-paced field of AI, both for AI-powered applications and AI-powered development. + +### Introducing Firebase AI Logic + +Vertex AI in Firebase is now known as Firebase AI Logic. And among many new things, it now allows you to use Vertex AI Gemini API (billing required with the pay-as-you-go Blaze pricing plan), or the newly added Gemini Developer API (billing optional with the no-cost Spark pricing plan) all from one single Dart SDK. For more details, see the [Firebase blog](https://firebase.blog/posts/2025/05/building-ai-apps). + +### DartPad and Gemini + +We’ve added Gemini support directly into [DartPad](https://dartpad.dev/), which brings the power of Gemini code assistance right to your finger-tips with no setup or installation necessary. + +<DashImage figure src="images/1bvMcF-Tc30i2dMr2jFfECA.webp" alt="Gemini-powered code generation in DartPad" caption="Gemini-powered code generation in DartPad" /> + + +That feature was built using the [Gemini API](https://ai.google.dev/). Its full source code is available in the DartPad GitHub repo and serves as a demonstration of how you can create AI-powered development tools. + +### Android Studio, VSCode, IntelliJ IDEA and Gemini + +Another place that now has Gemini support is Android Studio. The Flutter and Android Studio teams [collaborated](https://medium.com/flutter/gemini-in-android-studio-now-speaks-fluent-flutter-915dfec98274) on this so, as of the most recent Android Studio release (Meerkat Feature Drop), Gemini support for Dart and Flutter is now first-class. + +And if you’re a VSCode or IntelliJ IDEA user, rest easy. The [Gemini Code Assist](https://codeassist.google/) extension and a Google account brings Gemini-powered code assistance to your workflows right in VS Code. + +### MCP + +MCP is an exciting new open protocol that is gaining a lot of momentum for enabling AI agents to access and interact with various data sources and tools in a standardized way. + +<DashImage figure src="images/0n4GWT45AkStIiLZy.webp" alt="Model Context Protocol" caption="Model Context Protocol" /> + + +We are currently experimenting to see if it can enable us to provide more accurate and relevant code generation and allow tooling for complex tasks like fixing layout issues, managing dependencies, or even resolving runtime errors. + +## Wrap up + +A big thank you to all of you who have made Flutter what it is. We’re proud to continue the journey with you all to build the world’s best app framework. + +Have a great I/O, check out the [great session videos](https://io.google/2025/explore?topics=Flutter), and have fun with Flutter! \ No newline at end of file diff --git a/sites/www/content/blog/dart-sound-null-safety-technical-preview-2/images/0FE-q5RdXwPgtRVE5.webp b/sites/www/content/blog/dart-sound-null-safety-technical-preview-2/images/0FE-q5RdXwPgtRVE5.webp new file mode 100644 index 0000000000..b098d58c29 Binary files /dev/null and b/sites/www/content/blog/dart-sound-null-safety-technical-preview-2/images/0FE-q5RdXwPgtRVE5.webp differ diff --git a/sites/www/content/blog/dart-sound-null-safety-technical-preview-2/images/0vRDfouqMsEojq6t9.webp b/sites/www/content/blog/dart-sound-null-safety-technical-preview-2/images/0vRDfouqMsEojq6t9.webp new file mode 100644 index 0000000000..a2d998f53e Binary files /dev/null and b/sites/www/content/blog/dart-sound-null-safety-technical-preview-2/images/0vRDfouqMsEojq6t9.webp differ diff --git a/sites/www/content/blog/dart-sound-null-safety-technical-preview-2/index.md b/sites/www/content/blog/dart-sound-null-safety-technical-preview-2/index.md new file mode 100644 index 0000000000..fe46cefe95 --- /dev/null +++ b/sites/www/content/blog/dart-sound-null-safety-technical-preview-2/index.md @@ -0,0 +1,203 @@ +--- +title: "Dart sound null safety: technical preview 2" +description: "Announcing null-safe support for the Flutter framework" +publishDate: 2020-10-22 +author: mit-mit +image: images/0FE-q5RdXwPgtRVE5.webp +category: announcements +layout: blog +--- + +Dart serves a special role in Flutter, powering developer features such as hot reload, and enabling multi-platform apps for mobile, desktop, and web via Dart’s flexible compiler technology. We strive to make the Dart language the most productive for Flutter app developers; for example, we added [UI-as-code language constructs](https://medium.com/dartlang/announcing-dart-2-3-optimized-for-building-user-interfaces-e84919ca1dff) to optimize the Dart syntax for coding Flutter widget trees. + +In June we offered a [first tech preview](https://medium.com/dartlang/announcing-sound-null-safety-defd2216a6f3) of null safety for Dart. Today is another major milestone, which we’ve [looked forward to](https://medium.com/dartlang/announcing-dart-2-10-350823952bd5) for a while: We’re announcing a second tech preview of **sound null safety**, including support for the Flutter framework. + +Null safety is a major new productivity feature that helps you avoid null exceptions, a class of bugs that are often hard to spot. As an added bonus, this feature also enables a range of performance improvements. We’re really looking forward to your feedback. + +## Why null safety? + +Dart is a type-safe language. This means that when you get a variable of some type, the compiler can guarantee that it is of that type. But type safety by itself doesn’t guarantee that the variable is not null. + +Null errors are very common. A search on GitHub leads to thousands of issues caused by unexpected nulls in Dart code, and even more thousands of commits trying to fix those issues. Try to see if you can spot the nullability problems in the following Flutter app, imagining that `Config` and `WeatherService` are backend services used by the app: + +```dart +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + // Get data from services. Note: in a real application, + // these would be async calls, but we're using sync calls + // for simplicity. + final localizedAppName = Config.getAppName(); + final temperatures = WeatherService.getTemperatures(); + return MaterialApp( + home: Scaffold( + appBar: AppBar(title: Text(localizedAppName)), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Temperature next 3 days:'), + for (final t in temperatures) + Text(t.round().toString()), + ], + ), + ), + ), + ); + } +} +class Config { + static String getAppName() { ... } +} +class WeatherService { + static List<double> getTemperatures() { ...} +} +``` + +This app will certainly fail if `getAppName()` returns a null; in that case we’ll pass a null to the `Text` widget used in the title of `AppBar`. + +But there are more subtle cases to consider: `getTemperatures()` could also return null. In that case the for-loop will fail. Or `getTemperatures()` could return a list as expected, but that list might contain null values, in which case we’ll call `round()` on null, and the app will fail. + +The null safety feature makes these problems go away by [validating your code](https://nullsafety.dartpad.dev/28f1db5ef4401d9e063375e5c58f0f86) as you’re typing: + +<DashImage figure src="images/0FE-q5RdXwPgtRVE5.webp" alt="With null safety, Dart finds potential null errors in your code." caption="With null safety, Dart finds potential null errors in your code." /> + + +With null safety, you can reason about your code with more confidence. No more pesky runtime null dereferencing errors in deployed apps. Instead, you get static errors as you code. + +## Null safety principles + +Dart null safety support is based on the following three core design principles: + +1. **Non-nullable by default**. Unless you explicitly tell Dart that a variable can be null, it will be considered non-nullable. We chose this as the default because we found that non-null was by far the most common choice in APIs. + +1. **Incrementally adoptable**. There’s a lot of Dart code out there. It will be possible to migrate to null safety at a time when you choose to, and then incrementally, part by part. It will be possible to have null-safe and non-null-safe code in the same project. We’ll also provide tools to help you with the migration. + +1. **Fully sound**. Dart’s null safety is *sound*. This means that we can trust the type system: if it determines that something isn’t null, then it can *never* be null. This enables compiler optimizations. Once you migrate your whole project and your dependencies to null safety, you reap the full benefits of soundness — not only fewer bugs, but smaller binaries and faster execution. + +Let’s review these design principles in more detail. + +## 1. Non-nullable by default + +The core syntax is simple enough. Here are some non-nullable variables, declared in different ways. Remember, non-nullable is the default, so these declarations look like they do today, but their meaning changes. + +```dart +// In null-safe Dart, none of these can ever be null. +var widget = Text('Hello'); +final status = GetStatus(); +String m = ''; +``` + +Dart will make sure that you never assign null to any of the above variables. If you try to do `widget = null` a thousand lines later, you’ll get a static analysis error and red squiggly lines, and your program will refuse to compile. + +### Nullable variables + +If you want your variable to be nullable, you can use `?`, like this: + +```dart +// These are all nullable variables. +Text? t = Text('Hello'); // Can be null later. +final Status? s = getStatus(); // Maybe the function returns null. +String? n; // Is null at first. Can be null at any later time, too. +``` + +You can use the `?` syntax in function parameters and return values, too: + +```dart +// In function parameters. +void initialize(int? count) { + // It's possible that count is null. +} +// In function return values. +static List<double?>? getTemperatures() { + // Can return null instead of a List, and the list can contain nulls. +} +``` + +But, once again, the dream is that you’ll rarely have to use `?`. The majority of your types will be non-nullable. + +### Being productive with null safety + +Null safety isn’t just about safety. We also want you to be productive when using the feature, which means that the feature must be easy to use. For example, look at this code, which uses `if` to check for a null value: + +```dart +void honk(int? loudness) { + if (loudness == null) { + // No loudness specified, notify the developer + // with maximum loudness. + _playSound('error.wav', volume: 11); + return; + } + // Loudness is non-null, let's just clamp it to acceptable levels. + _playSound('honk.wav', volume: loudness.clamp(0, 11)); +} +``` + +Note how the Dart tools can detect that by the time we pass that if-statement, the `loudness` variable cannot be null. And so Dart lets us call the `clamp()` method without jumping through hoops. This convenience is enabled by something called flow analysis: the Dart analyzer goes through your code as if it was executing it, figuring out additional information about your code automatically. + +Here’s another example, which shows a case where Dart can be sure that a variable is non-null because we always assign a non-null value to it: + +```dart +class StatusLine extends StatelessWidget { + final Status status; + StatusLine({this.status: Status.failed}); + @override + Widget build(BuildContext context) { + // This local variable is non-nullable, but not initialized. + String statusText; + if (status == Status.ok) { + statusText = 'Update succeeded'; + } else { + statusText = 'Update failed'; + } + // By this point, Dart knows statusText isn't null, as we've + // assigned a value in both the if- and else-statements. Thus + // we can pass it to the Text widget -- which doesn't accept + // nullable Strings -- without getting an error. + return Text(statusText); + } +``` + +If you remove any of the assignments above (for example, by deleting the line `statusText = 'Update failed';`), Dart cannot guarantee that `statusText` will be non-null: you’ll get a static error and your code won’t compile. You can [give this a try in DartPad](https://nullsafety.dartpad.dev/ecc0f87fa5af5cc7ff30d8bd3e3b12e2). + +## 2. Incrementally adoptable + +Because null safety is such a fundamental change to our typing system, it would be extremely disruptive if we insisted on forced adoption. We want to let you decide when the time is right, so null safety is an opt-in feature: you’ll be able to use the latest Dart and Flutter releases without being forced to enable null safety before you’re ready to do so. You can even depend on packages that have already enabled null safety from an app or package that hasn’t yet. + +Once you choose to adopt, we strongly recommend migrating code in order, with the leaves of the dependency graph being migrated first. For example, if C depends on B which depends on A, migrate A to null safety first, then B, then C. This order applies whether A, B, and C are libraries, packages, or apps. + +Why is the order important? Although you can make some progress migrating code before your dependencies migrate, you risk having to do a second migration pass if your dependencies change their APIs during their migration. When we get to the beta release, we’ll provide tools to help you find out which of your dependencies have migrated. If you’re a package author, then to avoid the risk of breaking your APIs, wait until all of your dependencies have migrated before you publish a null-safe version. + +When your dependencies are ready, you can use our migration tool. The tool works by analyzing all of your existing code. The migration tool is interactive, so you can review the nullability properties that the tool has inferred. If you disagree with any of the tool’s conclusions, you can add nullability hints to change the inference. Adding a few migration hints can have a huge impact on migration quality. + +<DashImage figure src="images/0vRDfouqMsEojq6t9.webp" alt="The migration tool helps you interactively migrate your code to null safety." caption="The migration tool helps you interactively migrate your code to null safety." /> + + +## 3. Fully sound + +Once you’ve fully migrated, Dart’s null safety is **sound**. This means that Dart is 100% sure that in the above examples, the return variables, lists, and elements cannot be null. When Dart analyzes your code and determines that a variable is non-nullable, that variable is **always** non-nullable: if you inspect your running code in the debugger, you’ll see that non-nullability is retained at runtime. By contrast, some other implementations are unsound, and in many cases still need to perform runtime null checks. Dart shares ***sound* null safety** with Swift, but not very many other programming languages. + +The soundness of Dart’s null safety has another welcome implication: it means your programs can be smaller and faster. Because Dart is really sure that non-nullable variables are never null, Dart can optimize. For example, the Dart ahead-of-time (AOT) compiler can produce smaller and faster native code, because it doesn’t need to add checks for nulls when it knows that a variable isn’t null. + +Note that to get sound null safety, you’ll need to migrate your whole project and all of your dependencies to null safety. If part of your app or dependencies haven’t been migrated you’ll get partial null safety, which retains most of the checks but isn’t fully optimized and doesn’t guarantee that the app is fully safe. + +## The null safety roadmap + +When might null safety be ready for production use? Here is the current timeline: + +1. **Flutter experimentation with *technical preview 2***: This is today. Because we’ve successfully migrated the core Flutter framework to null safety, you’re able to try null safety to learn the new language feature and experiment with a [small Flutter sample](https://github.com/flutter/samples/tree/master/experimental/null_safety). If you’re a package author, you may also be able to do a trial migration, if you have a small dependency set that’s already been migrated by us. You need to pass an [experiment flag](https://dart.dev/tools/experiment-flags), shouldn’t use it in production, and shouldn’t publish any migrated packages. + +1. **Early package migration with *beta***: Later this year, we’ll complete performance tuning and have sufficient test coverage to give us confidence that the feature works as intended, and that backwards compatibility is solid. At that time we’ll publish a beta version of the feature, and you won’t need to pass the experiment flag. We hope to see package owners begin migration of their packages to null safety, which will give us one last round of validation that the feature is ready for a stable release. + +1. **Production use with *stable***: Next, we’ll address feedback from beta, fix any remaining issues, and then publish to stable. It’s hard to state a concrete timeline for this, but we’re thinking early next year. Once the feature is stable, we hope to see lots of adoption of null safety, with null-safe apps published to stores and many null-safe packages published to pub.dev in stable versions. + +## Try it now + +You can start experimenting with null safety today! To get started quickly, check out this special version of [DartPad with null safety](https://nullsafety.dartpad.dev/). + +If you’d like to try null safety in VS Code, Android Studio, or the terminal, check out the [Flutter null safety sample app](https://github.com/flutter/samples/tree/master/experimental/null_safety). This app contains execution instructions and two versions of a small weather app: one that doesn’t use null safety and has a few sporadic null errors in it, and one that uses null safety to ensure that these issues are handled. If you prefer experimenting with a new Flutter app, you can run `flutter create` and then follow the [experiment instructions](https://github.com/flutter/flutter/wiki/Experimenting-with-null-safety-in-Flutter) to enable null safety. Note that you will need a dev-channel Flutter SDK (version `1.24.0–3.0.pre` or later), as the current stable and beta versions of Flutter don’t support null safety. + +To learn more about the feature design, read our new [Understanding null safety](https://dart.dev/null-safety/understanding-null-safety) documentation. If you prefer a short video, check out [the null safety video](https://www.youtube.com/watch?v=ZxSyZHq8gUg&feature=youtu.be&list=PLjxrf2q8roU1rBlI9vz01K2324nzIhYgp&t=322) from the [Flutter Day event](https://events.withgoogle.com/flutter-day/#content) a few months ago. + +We’re very happy to bring sound null safety to Dart. Sound null safety is a distinctive feature of Dart that helps you write less error-prone code and get better performance. We hope you’ll [experiment](https://github.com/flutter/flutter/wiki/Experimenting-with-null-safety-in-Flutter) with the feature in tech preview, and [give us feedback](https://github.com/dart-lang/sdk/issues/new?title=Null%20safety%20feedback:%20[issue%20summary]&labels=NNBD&body=Describe%20the%20issue%20or%20potential%20improvement%20in%20detail%20here) through our issue tracker. Happy coding! \ No newline at end of file diff --git a/sites/www/content/blog/data.yaml b/sites/www/content/blog/data.yaml new file mode 100644 index 0000000000..4ab8478763 --- /dev/null +++ b/sites/www/content/blog/data.yaml @@ -0,0 +1,24 @@ +categories: + - slug: releases + label: Releases + showFeatured: true + - slug: announcements + label: Announcements + showFeatured: true + - slug: events + label: Events + showFeatured: false + - slug: spotlight + label: Spotlight + showFeatured: false + - slug: tutorial + label: Tutorial + showFeatured: false + - slug: other + label: Other + showFeatured: false +nextPostsTitle: "More from Flutter" +atom: + # The title used for links pointing readers at the blog Atom feed. + subscribeTitle: "Subscribe to the Flutter Blog Atom feed" + feedAuthor: "Flutter Team" diff --git a/sites/www/content/blog/decomposing-widgets-backdrop/images/0EhUXhOHx6i4KCuUj.webp b/sites/www/content/blog/decomposing-widgets-backdrop/images/0EhUXhOHx6i4KCuUj.webp new file mode 100644 index 0000000000..d05e547a11 Binary files /dev/null and b/sites/www/content/blog/decomposing-widgets-backdrop/images/0EhUXhOHx6i4KCuUj.webp differ diff --git a/sites/www/content/blog/decomposing-widgets-backdrop/images/0RLNPIlZaftyTRlhw.webp b/sites/www/content/blog/decomposing-widgets-backdrop/images/0RLNPIlZaftyTRlhw.webp new file mode 100644 index 0000000000..f4886015a0 Binary files /dev/null and b/sites/www/content/blog/decomposing-widgets-backdrop/images/0RLNPIlZaftyTRlhw.webp differ diff --git a/sites/www/content/blog/decomposing-widgets-backdrop/images/0RLdUfeFhMFa502tl.webp b/sites/www/content/blog/decomposing-widgets-backdrop/images/0RLdUfeFhMFa502tl.webp new file mode 100644 index 0000000000..5a604a04ea Binary files /dev/null and b/sites/www/content/blog/decomposing-widgets-backdrop/images/0RLdUfeFhMFa502tl.webp differ diff --git a/sites/www/content/blog/decomposing-widgets-backdrop/images/1in7MRIAKfRn-qDgJKc9XVw.jpeg b/sites/www/content/blog/decomposing-widgets-backdrop/images/1in7MRIAKfRn-qDgJKc9XVw.jpeg new file mode 100644 index 0000000000..d147838089 Binary files /dev/null and b/sites/www/content/blog/decomposing-widgets-backdrop/images/1in7MRIAKfRn-qDgJKc9XVw.jpeg differ diff --git a/sites/www/content/blog/decomposing-widgets-backdrop/index.md b/sites/www/content/blog/decomposing-widgets-backdrop/index.md new file mode 100644 index 0000000000..3014bfee27 --- /dev/null +++ b/sites/www/content/blog/decomposing-widgets-backdrop/index.md @@ -0,0 +1,281 @@ +--- +title: "Decomposing Widgets: Backdrop" +description: "While it’s easy to create custom widgets in Flutter, it’s often useful to examine existing widgets to better understand how to compose…" +publishDate: 2018-06-26 +author: mjohnsullivan +image: images/1in7MRIAKfRn-qDgJKc9XVw.jpeg +category: tutorial +layout: blog +--- + +While it’s easy to create custom widgets in Flutter, it’s often useful to examine existing widgets to better understand how to compose widgets to create more complex ones. + +[Hans Muller](https://github.com/HansMuller) and [Mary Xia](https://github.com/maryx) created Backdrop, a widget that implements the [Material Backdrop component](https://material.io/design/components/backdrop.html), and used in the Flutter Gallery, [Flutter’s Udacity course](https://www.udacity.com/course/build-native-mobile-apps-with-flutter--ud905) and the [Flutter Material Design talk](https://www.youtube.com/watch?v=hA0hrpR-o8U) at I/O 2018. Backdrop manages a front layer widget that overlays a back layer and can be opened and closed. + +<DashImage figure src="images/0RLNPIlZaftyTRlhw.webp" alt="Unit Converter app from Flutter’s Udacity course" caption="Unit Converter app from Flutter’s Udacity course" /> + + +Backdrop uses a number of Flutter widgets to accomplish this: Stack, Expanded, LayoutBuilder, Animation, Tween, and PositionedTransition. It serves as a good example of how to compose standard Flutter widgets together to create something more complex. + +I extracted Backdrop’s source code and wrote a couple of examples to demonstrate how it works. The revised code is [available on Github](https://github.com/mjohnsullivan/flutter-by-example/tree/master/16_panels), and I’d recommend you spend a couple of minutes playing with and examining the code before diving into the deconstruction below. + +I’ve also worked in both ValueNotifier and ScopedModel to demonstrate a couple of different techniques for handling state in Flutter apps. + +Let’s dig in and explore how this widget works. + +## Backdrop + +Backdrop is a StatefulWidget whose state manages the position and animation of the front layer. Let’s start by focusing on Backdrop’s build() function. + +``` +AnimationController _controller; + +Widget build(BuildContext context) { + return LayoutBuilder(builder: (context, constraints) { + final panelSize = constraints.biggest; + final closedPercentage = widget.frontHeaderVisibleClosed + ? (panelSize.height - widget.frontHeaderHeight) / panelSize.height + : 1.0; + final openPercentage = widget.frontPanelOpenHeight / panelSize.height; + +final panelDetailsPosition = Tween<Offset>( + begin: Offset(0.0, closedPercentage), + end: Offset(0.0, openPercentage), + ).animate(_controller.view); + +return Container( + child: Stack( + children: <Widget>[ + widget.backLayer, + SlideTransition( + position: panelDetailsPosition, + child: _BackdropPanel( + child: widget.frontLayer, + // panel configs + ), + ), + ], + ), + ); + }); +} +``` + + +The widget consists of a moveable front layer, stacked on top of an immovable back layer. The backLayer widget is placed first in the Stack widget so it appears underneath, followed by _BackdropPanel containing the frontLayer, wrapped in a SlideTransition. The [original version of Backdrop](https://github.com/flutter/flutter/blob/09276bea258737e11b109e227f70eac94a4e1691/examples/flutter_gallery/lib/gallery/backdrop.dart) used PositionTransition, but this was changed to avoid overflow issues when completely hiding the front layer. + +LayoutBuilder determines how much vertical space is available at run time, which can vary by screen size and density. This is then used to work out how much of the back layer is covered by the front layer when open. The Tween defines the open and closed positions of the front layer and uses the AnimationController to manage motion. + +The BackdropPanel widget implements the front layer with a Column containing the header, divider, and child. The child widget is wrapped in an Expanded to ensure that it fills the available space. + +The Material widget decorates the panel with a drop shadow and beveled edges using BorderRadius. + +``` +Widget build(BuildContext context) { + return Material( + elevation: 12.0, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(16.0), + topRight: Radius.circular(16.0), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: <Widget>[ + GestureDetector( + // ... + child: Container( + // ... + height: titleHeight, + child: title, + ), + ), + Divider(height: 1.0,), + Expanded(child: child,), + ], + ), + ); +} +``` + + +The remaining code in Backdrop primarily implements dragging and animations. BackdropState has an AnimationController where a value of 1 opens the front layer, and 0 closes it. + +``` +_controller = AnimationController( + duration: Duration(milliseconds: 300), + // 0 hides the panel; 1 shows the panel + value: (widget.panelVisible?.value ?? true) ? 1.0 : 0.0, + vsync: this, + ); +``` + + +The status of the controller can be queried to determine the state of the layer: + +``` +bool get _backdropPanelVisible => + _controller.status == AnimationStatus.completed || + _controller.status == AnimationStatus.forward; +``` + + +fling() can be called to open or close the layer: + +``` +void _toggleBackdropPanelVisibility() => _controller.fling( + velocity: _backdropPanelVisible + ? -_kFlingVelocity + : _kFlingVelocity + ); +``` + + +Dragging the layer is supported by _handleDragUpdate(). _handleDragEnd() determines whether the layer should animate open or closed, depending on drag direction, velocity, and position. These functions are used by the GestureDetector widget in BackdropPanel. + +``` +void _handleDragUpdate(DragUpdateDetails details) { + if (!_controller.isAnimating) + _controller.value -= details.primaryDelta / _backdropHeight; +} + +void _handleDragEnd(DragEndDetails details) { + if (_controller.isAnimating || + _controller.status == AnimationStatus.completed) return; + +final double flingVelocity = + details.velocity.pixelsPerSecond.dy / _backdropHeight; + if (flingVelocity < 0.0) + _controller.fling(velocity: math.max(_kFlingVelocity, + -flingVelocity)); + else if (flingVelocity > 0.0) + _controller.fling(velocity: math.min(-_kFlingVelocity, + -flingVelocity)); + else + _controller.fling( + velocity: + _controller.value < 0.5 + ? -_kFlingVelocity + : _kFlingVelocity); +} +``` + + +## ValueNotifier + +I extended the original implementation of Backdrop to take a ValueNotifier, which the widget uses to track the open or closed state of the panel. ValueNotifiers provide a mechanism by which objects can listen for changes to a value. + +ValueNotifier is being used here as a demonstration; a callback or other form of state management would be equally valid. + +A boolean value, representing the layer’s open/closed state, is held by ValueNotifier. Those widgets interested in the panel state can subscribe to the notifier and know when the panel is opened or closed. + +Firstly we ensure that the value is updated when the layer is opened or closed, by listening to the AnimationController’s status in Backdrop: + +``` +if (widget.panelVisible != null) { + _controller.addStatusListener((status) { + if (status == AnimationStatus.completed) + widget.panelVisible.value = true; + else if (status == AnimationStatus.dismissed) + widget.panelVisible.value = false; + }); +``` + + +Backdrop also listens for changes in the value, so that other widgets can trigger a change in the front layer’s state: + +``` +void _subscribeToValueNotifier() { + if (widget.panelVisible.value != _backdropPanelVisible) + _toggleBackdropPanelVisibility(); +} +``` + + +One subtle, but important, side effect of using and subscribing to ValueNotifiers is that when the app is hot reloaded, the widgets may get rebuilt and, if you’ve subscribed to the notifier in initState(), the subscription is no longer valid. To fix this, implement the StatefulWidget’s didWidgetUpdate() method and add a new listener: + +``` +void didUpdateWidget(Backdrop oldWidget) { + super.didUpdateWidget(oldWidget); + oldWidget.panelVisible?.removeListener(_subscribeToValueNotifier); + widget.panelVisible?.addListener(_subscribeToValueNotifier); +} +``` + + +## SimpleExample + +The SimpleExample widget shows how to use Backdrop in the simplest of cases. Clicking the button changes the value in ValueNotifier and triggers the front layer to open or close. The notifier is also used to change the Text widget in the back layer. + +<DashImage figure src="images/0EhUXhOHx6i4KCuUj.webp" alt="Simple example" caption="Simple example" /> + + +## ComplexExample + +ComplexExample demonstrates how to use Backdrop to show different content in the front layer and uses ScopedModel to track the content state. A Model is first defined to manage the front layer state. + +``` +enum FrontPanels { tealPanel, limePanel } + +class FrontPanelModel extends Model { + FrontPanelModel(this._activePanel); + FrontPanels _activePanel; + +FrontPanels get activePanelType => _activePanel; + +Widget get panelTitle => _activePanel == FrontPanels.tealPanel + ? Text('Teal Panel') + : Text('Lime Panel'); + +Widget get activePanel => + _activePanel == FrontPanels.tealPanel ? TealPanel() : LimePanel(); + +void activate(FrontPanels panel) { + _activePanel = panel; + notifyListeners(); + } +} +``` + + +The model is placed in the widget tree, using a ScopedModel widget, and the ScopedModelDescendant widgets provide access to the model. They rebuild whenever the model changes, unless the rebuildOnChange flag is set to false. + +``` +Widget build(BuildContext context) => ScopedModel( + model: FrontPanelModel(FrontPanels.firstPanel), + child: Scaffold(body: SafeArea(child: Panels()))); +... +Widget build(BuildContext context) { + return ScopedModelDescendant<FrontPanelModel>( + builder: (context, _, model) => Backdrop( + frontPanel: model.activePanel, + frontHeader: model.panelTitle, + ... + ), + ); +} +... +Center( + child: ScopedModelDescendant<FrontPanelModel>( + rebuildOnChange: false, + builder: (context, _, model) => RaisedButton( + child: Text('show first panel'), + onPressed: () { + model.activate(FrontPanels.firstPanel); + widget.frontPanelOpen.value = true; + }, + ))), +``` + + +The buttons in the back layer control which widget is displayed in the front layer. + +<DashImage figure src="images/0RLdUfeFhMFa502tl.webp" alt="Complex example" caption="Complex example" /> + + +## Final Thoughts + +What looks initially like a complicated widget is, in fact, a collection of standard Flutter widgets composed together to lend their individual functionality to the whole. This is one of Flutter’s most powerful features, the ability to rapidly build out custom, complex UIs from the toolbox of widgets provided by the platform. + +Check out the full source code [on GitHub](https://github.com/mjohnsullivan/flutter-by-example/tree/master/16_panels), and thanks again to Hans and Mary, the original creators of Backdrop. + +<DashImage figure src="images/1in7MRIAKfRn-qDgJKc9XVw.jpeg" /> diff --git a/sites/www/content/blog/deprecation-lifetime-in-flutter/images/0kp3Y8CnUirX421Fi.jpg b/sites/www/content/blog/deprecation-lifetime-in-flutter/images/0kp3Y8CnUirX421Fi.jpg new file mode 100644 index 0000000000..5939f604ce Binary files /dev/null and b/sites/www/content/blog/deprecation-lifetime-in-flutter/images/0kp3Y8CnUirX421Fi.jpg differ diff --git a/sites/www/content/blog/deprecation-lifetime-in-flutter/images/0n5eFBwWfgtkuLtDf.jpg b/sites/www/content/blog/deprecation-lifetime-in-flutter/images/0n5eFBwWfgtkuLtDf.jpg new file mode 100644 index 0000000000..d009f3e370 Binary files /dev/null and b/sites/www/content/blog/deprecation-lifetime-in-flutter/images/0n5eFBwWfgtkuLtDf.jpg differ diff --git a/sites/www/content/blog/deprecation-lifetime-in-flutter/index.md b/sites/www/content/blog/deprecation-lifetime-in-flutter/index.md new file mode 100644 index 0000000000..b60b4aa17e --- /dev/null +++ b/sites/www/content/blog/deprecation-lifetime-in-flutter/index.md @@ -0,0 +1,47 @@ +--- +title: "Deprecation Lifetime in Flutter" +description: "Reliably removing deprecated code on a scheduled basis." +publishDate: 2020-11-18 +author: piinks +image: images/0n5eFBwWfgtkuLtDf.jpg +category: announcements +layout: blog +--- + +A new year is approaching, and on the Flutter team, we’re getting ready for a clean start to 2021. + +In preparation, we’re going to remove some of the deprecated APIs in the Flutter framework. + +Removing deprecations in the Flutter framework isn’t new. I’ve done it myself [in the past](https://github.com/flutter/flutter/pull/57065). A [quick search](https://github.com/flutter/flutter/pulls?q=is%3Apr+is%3Aclosed+issues%3A+66020+64483+57065+51240+24115+67478) shows that deprecations were removed without a defined timeline or schedule because [our deprecation policy](https://github.com/flutter/flutter/wiki/Tree-hygiene#deprecation) doesn’t stipulate when to remove them. Because we want to provide a consistent and reliable experience for our users, we decided to create a standard lifetime for deprecations. + +This article discusses our current policies for breaking changes and deprecations, how they’re put into practice, and how they’re evolving to provide a better experience to our users. + +## Breaking changes in Flutter + +<DashImage figure src="images/0kp3Y8CnUirX421Fi.jpg" alt="Photo by [CHUTTERSNAP](https://unsplash.com/@chuttersnap?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)" caption="Photo by [CHUTTERSNAP](https://unsplash.com/@chuttersnap?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)" /> + + +The process for deprecating code in Flutter begins with [our breaking change policy](https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes). Deprecations are considered breaking changes, which means that they must follow the [public design document](https://flutter.dev/docs/resources/design-docs) guidelines which requires a period for discussion. (The design document for this proposal is [Implementing a Lifetime for Deprecations](https://docs.google.com/document/d/1Gc3ecrMghzc7WU4pgzKB8uBaTPpRdWfozn0otBbxR7s/edit).) In addition to providing a design document, we reach out on the [Flutter Contributor Discord](https://github.com/flutter/flutter/wiki/Chat) and popular groups like [flutter-announce](https://groups.google.com/g/flutter-announce) and [flutter-dev](https://groups.google.com/g/flutter-dev), all before making any changes. This is one of my favorite parts of the process as an engineer: I get to hear from you. Our wonderful community is incredibly engaged with this process, and we are better for it. On multiple occasions, I’ve seen the design of a feature improve greatly — all thanks to feedback from our community — after publicizing it through these channels. + +Authors of breaking changes migrate customer code affected by a breaking change. In the last update to our breaking change policy, [we invited](https://groups.google.com/g/flutter-announce/c/Z09a317E21o) you to submit your tests to our [customer test repo](https://github.com/flutter/tests). These tests are our signal that a change is breaking, and we are committed to working with the test authors to migrate their code before making the change. + +We also provide [migration guides](https://flutter.dev/docs/release/breaking-changes) for our breaking changes. These guides are intended to help users migrate their code after updating to the latest version of Flutter. If you’ve seen one of our [release announcements](https://medium.com/flutter/announcing-flutter-1-22-44f146009e5f), we also call out breaks in a given release, and we point users to helpful resources for updating their applications. + +In a [past survey](https://medium.com/flutter/insights-from-flutters-first-user-survey-of-2019-3659b02303a5), we asked users about their preferences for breaking changes. We learned that our users thought breaking changes made code cleaner and were valuable to them, which is why we usually prefer a breaking change over a deprecation. According to the study: + +Overall, 78% said that they prefer breaking changes that result in the cleanest possible API; this also makes the API easier to learn and use. + +Our breaking change policy is based on this study. As always, there are exceptions to every rule. I recently deprecated [some very popular methods](https://github.com/flutter/flutter/pull/67947) in favor of a new widget. If we know a particular change is a significant break that affects many users, we’ll deprecate. Deprecating allows for a migration grace period, while still providing all of the tools generated by the breaking change policy. + +## Aging deprecations + +<DashImage figure src="images/0n5eFBwWfgtkuLtDf.jpg" alt="Photo by [Dilyara Garifullina](https://unsplash.com/@dilja96?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)" caption="Photo by [Dilyara Garifullina](https://unsplash.com/@dilja96?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)" /> + + +The migration grace period begins when the deprecation is released on the [stable channel](https://flutter.dev/docs/development/tools/sdk/releases?tab=windows). The timeline for when the grace period ends is 1 calendar year or 4 stable releases, whichever is longer. In order to come up with a timeline that we thought was comfortable, we looked at our growth and adoption, and we evaluated some of the Flutter applications and packages that are open sourced. + +We want our users to be able to count on this schedule, rather than wondering if the next release will bring an unexpected barrier to upgrading because a deprecation disappeared. We also want to make sure that we communicate these changes well ahead of time, and we commit to announcing deprecations reaching the end of their lifetimes before we start removing them. In that spirit, I’d like to share with you [the first batch of deprecations that are reaching their end of life](http://flutter.dev/go/deprecation-lifetime), now that Flutter 1.22 is available. + +Because this is the first time that we’re putting this policy into practice, there are quite a few deprecations — some pre-dating Flutter 1.0! Several of these deprecations also pre-date our breaking change policy. We’ll be working on migration tools and guidance for all of them. Expect to see these deprecations removed from the Flutter framework by our next stable release, with announcements occurring in our usual channels as they’re removed. + +While we get underway implementing these changes, we hope to hear from you! We’re always accepting submissions to our customer test repo where we work with you to migrate your code before making a break. To learn more about the test repo and how to submit your tests, take a look at the [Flutter tests README](https://github.com/flutter/tests/blob/master/README.md). As we make these changes, we’ll use the [Flutter Contributor Discord](https://github.com/flutter/flutter/wiki/Chat), [flutter-announce](https://groups.google.com/g/flutter-announce), and [flutter-dev](https://groups.google.com/g/flutter-dev) channels to notify you and request your feedback. So, if you haven’t joined one of these groups, please do! We look forward to hearing from you and working together. \ No newline at end of file diff --git a/sites/www/content/blog/developing-flutter-apps-for-large-screens/images/0-7rhN0eWj8p1EZ3_.webp b/sites/www/content/blog/developing-flutter-apps-for-large-screens/images/0-7rhN0eWj8p1EZ3_.webp new file mode 100644 index 0000000000..caf18f7c21 Binary files /dev/null and b/sites/www/content/blog/developing-flutter-apps-for-large-screens/images/0-7rhN0eWj8p1EZ3_.webp differ diff --git a/sites/www/content/blog/developing-flutter-apps-for-large-screens/images/04I4h-v1nPuUbxBt4.webp b/sites/www/content/blog/developing-flutter-apps-for-large-screens/images/04I4h-v1nPuUbxBt4.webp new file mode 100644 index 0000000000..58a1e0b84f Binary files /dev/null and b/sites/www/content/blog/developing-flutter-apps-for-large-screens/images/04I4h-v1nPuUbxBt4.webp differ diff --git a/sites/www/content/blog/developing-flutter-apps-for-large-screens/images/0CUG7wMHjzHSFKMpV.webp b/sites/www/content/blog/developing-flutter-apps-for-large-screens/images/0CUG7wMHjzHSFKMpV.webp new file mode 100644 index 0000000000..2a957056b7 Binary files /dev/null and b/sites/www/content/blog/developing-flutter-apps-for-large-screens/images/0CUG7wMHjzHSFKMpV.webp differ diff --git a/sites/www/content/blog/developing-flutter-apps-for-large-screens/images/0FkMJSwc7jtu4K7HR.webp b/sites/www/content/blog/developing-flutter-apps-for-large-screens/images/0FkMJSwc7jtu4K7HR.webp new file mode 100644 index 0000000000..1193263bc9 Binary files /dev/null and b/sites/www/content/blog/developing-flutter-apps-for-large-screens/images/0FkMJSwc7jtu4K7HR.webp differ diff --git a/sites/www/content/blog/developing-flutter-apps-for-large-screens/images/1T0mBXs1X6YdvCQmQlP5ryg.webp b/sites/www/content/blog/developing-flutter-apps-for-large-screens/images/1T0mBXs1X6YdvCQmQlP5ryg.webp new file mode 100644 index 0000000000..d6717bb666 Binary files /dev/null and b/sites/www/content/blog/developing-flutter-apps-for-large-screens/images/1T0mBXs1X6YdvCQmQlP5ryg.webp differ diff --git a/sites/www/content/blog/developing-flutter-apps-for-large-screens/index.md b/sites/www/content/blog/developing-flutter-apps-for-large-screens/index.md new file mode 100644 index 0000000000..09e42d3fe1 --- /dev/null +++ b/sites/www/content/blog/developing-flutter-apps-for-large-screens/index.md @@ -0,0 +1,163 @@ +--- +title: "Developing Flutter apps for Large screens" +description: "How to optimize your Flutter app to meet Android’s large screen requirements" +publishDate: 2023-09-26 +author: mariam_hasnany +image: images/04I4h-v1nPuUbxBt4.webp +category: announcements +layout: blog +--- + +Last year, we partnered with the team at gSkinner to develop [Wonderous](https://wonderous.app/), a reference app to showcase the high-quality experiences possible with Flutter. + +[One of the goals for creating Wonderous](https://medium.com/flutter/wonderous-explore-the-world-with-flutter-f43cce052e1) was to provide an open-source example that demonstrates best practices. In that same spirit, we audited Wonderous against [Android’s large screen guidelines](https://developer.android.com/docs/quality-guidelines/large-screen-app-quality). We wanted to ensure a high-quality Flutter app across all Android devices. Android has three tiers for meeting their guidelines: + +**Tier 3** — large screen ready — app runs on full screen (or full window in multi-window mode), but app layout might not be ideal + +**Tier 2** — large screen optimized — app layout is optimized for all screen sizes and handles external input devices + +**Tier 1** — large screen differentiated — app designed specifically with foldables in mind, ensuring the layout supports table-top mode with the hinge + +In January 2023, we updated the [Wonderous app to adapt to different device formats](https://medium.com/flutter/adapting-wonderous-to-larger-device-formats-ac51e1c00bc0). While these updates made the app “large screen ready”, the app wasn’t yet “large screen optimized”. + +So we again engaged gSkinner to elevate Wonderous to Tier 2 (or better) and optimized for [Android’s new large screen pixel devices](https://android-developers.googleblog.com/2023/06/the-new-pixel-fold-pixel-tablet-are-here-optimizing-for-large-screens.html). + +In this article, we share what we learned and what you should consider as you develop your app to meet Android’s large screen guidelines. + +## Why large are screens important + +Before diving into how we optimized Wonderous to meet Android’s Large Screen guidelines, let’s understand the benefits to supporting large screens. + +When we say [large screens](https://developer.android.com/guide/topics/large-screens/get-started-with-large-screens), we mean tablets, foldables, and ChromeOS devices running Android. Demand for large screens is increasing. Today, more than [270 million active large screen](https://developer.android.com/large-screens#:~:text=More%20than%20270%20million%20large%20screen%20Android%20devices) and foldable devices run on Android. + +Why this matters for developers: + +* Increases business metrics, such as increased user engagement on tablets for apps with layouts optimized to fill the full screen. + +* Improves [Technical quality](https://developer.android.com/quality/technical) to increase visibility for your app in the Play Store on these devices. Recent [Play Store updates](https://android-developers.googleblog.com/2022/03/helping-users-discover-quality-apps-on.html) show ratings by device type and notifies users when an app lacks large screen support. + +## Learnings from optimizing Wonderous + +Another goal for the Wonderous app is to serve as a source for creating learning materials and documentation. In this section, we share the details where Wonderous didn’t address large-screen requirements. These issues might be common problems Flutter developers encounter so we share how we fixed them. + +## App continuity & configuration changes + +**Requirement:** Apps should change orientation and retain or restore state as the device rotates, changes window size or folds and unfolds. + +### Problem: Folding/Unfolding causes state-loss + +When Wonderous was folded and unfolded, the app experienced state-loss and would always go back to the initial screen. This issue took a long time for us to figure out. We first assumed this was default behavior in Flutter with folding/unfolding a device, but couldn’t reproduce it with a Flutter counter app. We then realized it might be a plugin that was causing state loss, specifically the [flutter_inappwebview](https://pub.dev/packages/flutter_inappwebview) plugin. + +**Fix:** Replace the flutter_inappwebview plugin with [webview_flutter](https://pub.dev/packages/webview_flutter), which fixed maintaining state during device configuration. + +**Learning:** Your app should not lose state when the device is folded/unfolded, Flutter should maintain state by default. If your app is losing state, verify that the plugins you use support large screen devices. A native extension could cause state-loss when the device changes position. + +### Problem: Maintaining scroll position state + +We thought we might need a lot of boilerplate code to ensure that a list maintained its scroll position when the screen size changed. We first used the key:PageStorageKey API and it fixed most of our list views. The “learnings” section below describes the situation where you need to go further. + +<DashImage figure src="images/04I4h-v1nPuUbxBt4.webp" alt="Collections page in Wonderous app not maintaining scroll position" caption="Collections page in Wonderous app not maintaining scroll position" /> + + +**Fix:** Use the ****key: PageStorageKey API to maintain scroll position for lists that keep their layout the same when device orientation changes. ****By [adding a PageStorageKey to the collections page](https://github.com/gskinnerTeam/flutter-wonderous-app/blob/8a29d6709668980340b1b59c3d3588f123edd4d8/lib/ui/screens/wonder_events/widgets/_events_list.dart#L64) the scroll position of the SingleChildScrollView widget [will be stored automatically](https://api.flutter.dev/flutter/widgets/PageStorage-class.html)**.** + +**Learning:** While key: PageStorageKey offers an elegant way to maintain scroll position with a list, it doesn’t work if your List changes layout from vertical to horizontal. The previous GIF shows what happened with some of the Wonderous pages. In this case, you might have to [do a bit of math and change the scroll position on screen rotation](https://github.com/gskinnerTeam/flutter-wonderous-app/blob/34e49a08084fbbe69ed67be948ab00ef23819313/lib/ui/screens/collection/widgets/_collection_list.dart#L39). + +## Multi-window & split screen + +**Requirement:** App has full functionality running in multi-window mode. The app updates its UI, continuing to play media, for example, when the app doesn’t have top focus. + +### Problem: Letterboxing due to split screen view + +With the advent of[ “screen splitting”](https://developer.android.com/guide/topics/large-screens/multi-window-support), the user can, with some orientations and aspect ratios, put the app into less than two inches of vertical space. When the screen was split to ⅓ of the display area, the Wonderous app was [letterboxed](https://developer.android.com/guide/topics/large-screens/large-screen-compatibility-mode#letterboxing) or positioned in the center of the display area with a solid color background filling the unused area (shown below). + +<DashImage figure src="images/0CUG7wMHjzHSFKMpV.webp" alt="Wonderous app being letterboxed in split-screen mode on Pixel Fold" caption="Wonderous app being letterboxed in split-screen mode on Pixel Fold" /> + + +This happened because we chose to disable landscape on smaller screens by using MediaQuery to determine the size of the device. Unfortunately, MediaQuery doesn’t actually give you the screen size when you’re in split-screen mode and mistakenly identifies large devices with split-screens as small devices. + +**Fix:** We added a new API to Flutter to add support for [Android’s new window size class](https://developer.android.com/guide/topics/large-screens/support-different-screen-sizes#window_size_classes). By switching to the Display API we were able to get the full screen size and accurately distinguish a small device from a large device with multiple windows. With this API, you can retrieve various properties of a display. The new [FlutterView.display](https://master-api.flutter.dev/flutter/dart-ui/FlutterView/display.html) getter returns a [Display](https://master-api.flutter.dev/flutter/dart-ui/Display-class.html) object. The Display object reports the physical size, the device pixel ratio, and the refresh rate of the display. To check out an example that uses the new API, visit the [setPreferredMethod API](https://master-api.flutter.dev/flutter/services/SystemChrome/setPreferredOrientations.html). + +**Learning:** When disabling landscape on smaller devices (using SystemChrome.setPreferredOrientations()), use the Display API to determine the device size rather than MediaQuery. + +## Keyboard, mouse and trackpad input + +**Requirement:** Apps should support basic mice or trackpads, and keyboard shortcuts. Main user flows should support keyboard navigation to ensure accessibility. + +### Problem: Focus states + +Flutter’s Material buttons handle basic focus states by default. In the case of Wonderous, we changed the default styling of the Material buttons to set the overlayColor to transparent. This ended up breaking the focus state of the buttons. With large screen devices that have keyboards and trackpads, your app must handle navigation and provide accessible solutions for those peripheral devices. + +**Fix:** [Changed the button style](https://github.com/gskinnerTeam/flutter-wonderous-app/blob/8a29d6709668980340b1b59c3d3588f123edd4d8/lib/ui/common/controls/buttons.dart#L143) to have an outline when the button is focused. We used the FocusNode [hasFocus property](https://api.flutter.dev/flutter/widgets/FocusNode/hasFocus.html) to check when the button node had focus input and added a border to the button. + +**Learning:** Make sure to implement a focus state for any custom buttons or gesture detectors in your app. If you change the default Material button styles, make sure you are testing for keyboard focus states, and implementing your own if needed. + +### Problem: Mouse scroll slow + +The mouse scroll wheel didn’t work well on some devices throughout the app. A user had to move the scroll wheel a lot for the screen to respond . This was a known[ issue](https://github.com/flutter/flutter/issues/82973), thanks to our community. We noticed that it was more pronounced on tablets and foldables. + +<DashImage figure src="images/0-7rhN0eWj8p1EZ3_.webp" alt="Scrolling Wonderous app with a mouse is slow" caption="Scrolling Wonderous app with a mouse is slow" /> + + +**Fix:** We [fixed](https://github.com/flutter/engine/pull/44724) it in the framework since it affected all Flutter apps and it will be in the next Flutter stable release. + +**Learning:** Sometimes the issue is not unique to your app and needs to be fixed by the Flutter team :) + +## App UX layout + +**Requirement:** Apps should provide adaptive layouts and responsive visual elements that change for available screen space or device orientation. + +### Problem: Navigation rail + +In the first iteration of Wonderous, we disabled the rotation of the app on small form factors since the app lacked proper landscape support on smaller screens. As mentioned earlier, we fixed the app to be able to support landscape mode for smaller screens during split-screen mode on large screen devices. While this approach fixed the letterboxing issue, it became more obvious that the app’s navigation lacked the proper ergonomics for larger screens. + +<DashImage figure src="images/0FkMJSwc7jtu4K7HR.webp" alt="Before using Material’s Navigation rail" caption="Before using Material’s Navigation rail" /> + + +This required the app to layout the navigation close to the edges of the screen where it is easier to reach, such as the left side edge of a tablet to free up precious vertical space for app content in landscape mode. + +**Fix:** Update the design and implement a navigation similar to [Material’s Navigation rail](https://material.io/components/navigation-rail). + +**Learning:** Make sure the layouts and navigation in your app can function properly in large screen devices and ~2” of vertical space if the screen is in split-mode. To make this feasible use [adaptive widgets](https://docs.flutter.dev/ui/layout/responsive/building-adaptive-apps) like Material’s Navigation rail**.** + +<DashImage figure src="images/1T0mBXs1X6YdvCQmQlP5ryg.webp" alt="After using Material’s Navigation rail" caption="After using Material’s Navigation rail" /> + + +## Other things to consider + +### Problem: Camera preview and media projection + +[To achieve Android’s Tier 3 ](https://developer.android.com/docs/quality-guidelines/large-screen-app-quality#T3-7)status, your app needs to support camera preview and media projection in different orientations, screen sizes, and in multi-window mode. Since Wonderous doesn’t have a camera feature, this requirement didn’t apply. + +**Learning:** [Showing camera previews](https://developer.android.com/guide/topics/large-screens/large-screen-app-compatibility#camera_preview) can be surprisingly complex, but the [Jetpack CameraX library](https://developer.android.com/training/camerax) handles many of these details for you. In Flutter 3.10, we added preliminary support for CameraX to our Flutter camera plugin. To try it, opt in by adding the following line to your pubspec.yaml file. + +```yaml +Dependencies: +camera: ^0.10.4 # Latest camera version +camera_android_camerax: ^0.5.0 +``` + + +### Not just for Android + +While these fixes and learnings are presented for Android’s large screen devices, some might apply to other platforms. They can make your app run and feel better on iPads, desktop browsers, and desktop devices. The same learnings apply: leveraging the PageStorageKey API, using adaptive widgets and layouts, or ensuring the plugins you use work well with large form factors. + +## In summary + +Our main goal for optimizing the Wonderous app for [large screens](https://developer.android.com/guide/topics/large-screens/get-started-with-large-screens) was to demonstrate best practices for developing Flutter apps that provide a great experience across all Android devices. + +In the process of meeting Android’s comprehensive set of requirements for [Tier 2 status](https://developer.android.com/docs/quality-guidelines/large-screen-app-quality#large_screen_optimized), we discovered how to fix problems that might be common in other Flutter apps running on large screens. Here are some of the key learnings: + +* Maintain and restore [app state](https://developer.android.com/jetpack/compose/state#store-state) when changing device orientation or folding/unfolding a device + +* Ensure your app isn’t letterboxed by accurately determining the screen size using [Display API](https://master-api.flutter.dev/flutter/services/SystemChrome/setPreferredOrientations.html) + +* Handle external [input devices](https://developer.android.com/guide/topics/large-screens/input-compatibility-large-screens), such as scrolling with a mouse or keyboard + +* Design your app layout specifically with large screen devices in mind, such as leveraging [Material’s navigation rails](https://material.io/components/navigation-rail) + +You can learn more about Android’s [Large screen app quality](https://developer.android.com/docs/quality-guidelines/large-screen-app-quality) guidelines on Android’s developer site. + +We hope you’ll leverage [Wonderous](https://wonderous.app/) as a reference while developing your own Flutter apps to meet Android’s large screen requirements. + +If you have any feedback on the app, please [file an issue on Github](https://github.com/gskinnerTeam/flutter-wonderous-app). If you have any thoughts on Flutter’s Android support or supporting large screen devices, feel free to message me on [X](https://twitter.com/marihasnany) (@marihasnany). \ No newline at end of file diff --git a/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/images/01xmX3Sfp_rLOAwwl.webp b/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/images/01xmX3Sfp_rLOAwwl.webp new file mode 100644 index 0000000000..0d287b48e5 Binary files /dev/null and b/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/images/01xmX3Sfp_rLOAwwl.webp differ diff --git a/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/images/06d-qda9iAUu26wjA.webp b/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/images/06d-qda9iAUu26wjA.webp new file mode 100644 index 0000000000..74d7febfae Binary files /dev/null and b/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/images/06d-qda9iAUu26wjA.webp differ diff --git a/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/images/0UjrF-d2-BWLQyfEo.webp b/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/images/0UjrF-d2-BWLQyfEo.webp new file mode 100644 index 0000000000..c68f88d0ad Binary files /dev/null and b/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/images/0UjrF-d2-BWLQyfEo.webp differ diff --git a/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/images/0VO7YjLAkYQsVLIWj.webp b/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/images/0VO7YjLAkYQsVLIWj.webp new file mode 100644 index 0000000000..07c437ef68 Binary files /dev/null and b/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/images/0VO7YjLAkYQsVLIWj.webp differ diff --git a/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/images/0WOcQ1I3-ThMzDyld.webp b/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/images/0WOcQ1I3-ThMzDyld.webp new file mode 100644 index 0000000000..5aee095433 Binary files /dev/null and b/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/images/0WOcQ1I3-ThMzDyld.webp differ diff --git a/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/images/0eSjSmRfA6LMzx_Yc.webp b/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/images/0eSjSmRfA6LMzx_Yc.webp new file mode 100644 index 0000000000..a0d1da629b Binary files /dev/null and b/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/images/0eSjSmRfA6LMzx_Yc.webp differ diff --git a/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/index.md b/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/index.md new file mode 100644 index 0000000000..1a2e03956b --- /dev/null +++ b/sites/www/content/blog/directional-animations-with-built-in-explicit-animations/index.md @@ -0,0 +1,239 @@ +--- +title: "Directional animations with built-in explicit animations" +description: "Hi! In our previous episodes, we learned how to do some awesome animations using Flutter’s implicit animations. AnimatedFoo and…" +publishDate: 2020-01-10 +author: fitzface +image: images/01xmX3Sfp_rLOAwwl.webp +category: tutorial +layout: blog +--- + +*To watch this post in video form, check out our YouTube video [here](https://www.youtube.com/watch?v=CunyH6unILQ&t=1s).* + +Hi! In our [previous](https://medium.com/flutter/flutter-animation-basics-with-implicit-animations-95db481c5916) [posts](https://medium.com/flutter/custom-implicit-animations-in-flutter-with-tweenanimationbuilder-c76540b47185), we learned how to do some awesome animations using Flutter’s implicit animations. `AnimatedFoo` and `TweenAnimationBuilder` gave you the ability to drop some basic animations into your app. These animations typically go in one direction, “tweening” from a start to an end, where they stop. Behind the scenes, Flutter is taking control, assuming intentions and disposing of any need for you to worry about the transition from one thing to the next. + +This works perfectly for many animation goals, but sometimes that ever-forward arrow of time leaves us feeling temporally locked. So, as we pause and contemplate the laws of thermodynamics and the inevitable heat death of the universe, wouldn’t it be nice if we could reverse time, and do it all again? + +Enter our first foray into Flutter’s explicit animations! We won’t be building any time machines today, but we will be learning how to gain a bit more control over your animations using Transition widgets. + +Transition widgets are a set of Flutter widgets whose names all end in — you guessed it —Transition. [`ScaleTransition`](https://api.flutter.dev/flutter/widgets/ScaleTransition-class.html), [`DecoratedBoxTransition`](https://api.flutter.dev/flutter/widgets/DecoratedBoxTransition-class.html), [`SizeTransition`](https://api.flutter.dev/flutter/widgets/SizeTransition-class.html), and more. They look and feel a lot like our `AnimatedBlah` widgets. [`PositionedTransition`](https://api.flutter.dev/flutter/widgets/PositionedTransition-class.html), for example, animates a widget’s transition between different positions. This is much like [`AnimatedPositioned`](https://api.flutter.dev/flutter/widgets/AnimatedPositioned-class.html), but there is one major difference: these Transition widgets are extensions of [`AnimatedWidget`](https://api.flutter.dev/flutter/widgets/AnimatedWidget-class.htmlhttps://api.flutter.dev/flutter/widgets/AnimatedWidget-class.html). This makes them *explicit animations*. + +But, what does that really mean for us as app developers? Let’s step through what makes these animations tick. + +Here, we’ll be creating an animation of galactic proportions, using this starting image. But, in this initial unanimated state, it doesn’t feel very galactic. Our first quest: to mix in some rotation. + +<DashImage figure src="images/01xmX3Sfp_rLOAwwl.webp" alt="*An image of Fitz’s galaxy just sitting there, **not** rotating.*" caption="*An image of Fitz’s galaxy just sitting there, **not** rotating.*" /> + + +## `RotationTransition` as an example + +The [`RotationTransition`](https://api.flutter.dev/flutter/widgets/RotationTransition-class.html) widget is a handy one that takes care of all of the trigonometry and transformations math to make things spin. Its constructor only takes three things: + +``` +// [Most of] RotationTransition’s constructor +RotationTransition({ + Widget child, + Alignment alignment, + Animation<double> turns, +}) +``` + + +First is a child —the widget we want to rotate. The galaxy fits, so we’ll put it there: + +``` +RotationTransition( + child: GalaxyFitz(), + alignment: null, /*TODO*/ + turns: null, /*TODO*/ +) +``` + + +Next, we need to give `RotationTransition` the point our galaxy rotates around. Our galaxy’s black hole is roughly in the middle of the image where we’d normally expect. So, we’ll give an `alignment` of center, making all of our rotational math “aligned” to that point. + +``` +RotationTransition( + child: GalaxyFitz(), + alignment: Alignment.center, + turns: null, /*TODO*/ +) +``` + + +Last, what is this mysteriously named turns property? The API docs tell us this is… an `Animation`?!? Weren’t we creating an animation? + +<DashImage figure src="images/06d-qda9iAUu26wjA.webp" alt="*The* [`RotationTransition`](https://api.flutter.dev/flutter/widgets/RotationTransition-class.html) *docs tell us that turns is of type Animation.*" caption="*The* [`RotationTransition`](https://api.flutter.dev/flutter/widgets/RotationTransition-class.html) *docs tell us that turns is of type Animation.*" /> + + +Not to worry! This is part of what makes `RotationTransition`, and all the other Transition widgets, an explicit animation. We *could* accomplish the same rotation effect with an `AnimatedContainer` and a transform, but then we’d rotate once and then stop. With our explicit animations, we have control of time and can make it so that our galaxy never stops spinning. + +<DashImage figure src="images/0WOcQ1I3-ThMzDyld.webp" alt="*Astronomical tip of the day: Most galaxies take **a bit** longer than 5 seconds to complete one rotation.*" caption="*Astronomical tip of the day: Most galaxies take **a bit** longer than 5 seconds to complete one rotation.*" /> + + +The turns property expects something that gives it a value and notifies it when that value changes. An `Animation<double>` is just that. For `RotationTransition`, the value corresponds to how many times we’ve turned, or more specifically, the percentage of one rotation completed. + +<DashImage figure src="images/0VO7YjLAkYQsVLIWj.webp" alt="*It would take the solar system around 30 million years to complete 12.6% of a rotation around the Milky Way. Our Flutter Galaxy will spin **slightly** faster than that.*" caption="*It would take the solar system around 30 million years to complete 12.6% of a rotation around the Milky Way. Our Flutter Galaxy will spin **slightly** faster than that.*" /> + + +## Creating an AnimationController + +One of the easiest ways to get an `Animation<double>` is to create an `AnimationController`, which is a [controller for an animation](https://api.flutter.dev/flutter/animation/AnimationController-class.html). This controller handles listening for ticks¹ and gives us some useful controls over what the animation is doing. + +We’ll need to create this in a stateful widget because keeping a handle on the controller will be important in our not-too-distant future. Because `AnimationController` also has its own state to manage, we initialize it in initState, and dispose of it in dispose. + +```dart +class _TimeMachineState extends State<TimeMachine> { + AnimationController _animationController; + + @override + void initState() { + super.initState(); + + _animationController = AnimationController( + // ... + ); + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } +} +``` + +There are two parameters we must give to `AnimationController`’s constructor. The first is a duration, which is how long our ̶t̶i̶m̶e̶ ̶m̶a̶c̶h̶i̶n̶e̶ animation lasts. The whole reason we’re here is that we need an object to tell us how far along we are in a single rotation. By default, `AnimationController` “emits” values from `0.0` to `1.0`. How many and how granular those values are depends on how long we want a single rotation to take. Fortunately, Dart gives us a `Duration` class to use. For the sake of this demo, we should have the galaxy spinning somewhere between 5 seconds and 230 million years per rotation. How about 15 seconds per turn then? + +``` +_animationController = AnimationController( + duration: Duration(seconds: 15), + // TODO: finish constructing me. +); +``` + + +The next required parameter is `vsync`. If you’re here from the future, welcome back! We hope you already know everything about vsync. For those who came here from the past, we’ll just say that this is what gives Flutter a reference to the object to notify about changes. `this` is that thing, and it needs to mix in some ticker provider code. A future post will dive into more detail about vsync and ticker providers. + +```dart +class _TimeMachineState extends State<TimeMachine> +with SingleTickerProviderStateMixin { + AnimationController _animationController; + + @override + void initState() { + super.initState(); + + _animationController = AnimationController( + duration: Duration(seconds: 15), + vsync: this, + ); + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } +} +``` + +If we left things at that, nothing much happens. That’s because we’ve been given a controller, but haven’t pushed any of its buttons! We want our galaxy to spin forever, right? For that, we’ll just ask the controller to continually repeat the animation. + +``` +_animationController = AnimationController( + duration: Duration(seconds: 15), + vsync: this, +)..repeat(); +``` + + +Finally, we can go back and replace that null we left lingering around, by passing the animation controller to the `turns` parameter in our `RotationTransition`. + +``` +RotationTransition( + child: GalaxyFitz(), + alignment: Alignment.center, + turns: _animationController, +) +``` + + +And, although we now have an infinitely rotating galaxy, this still doesn’t quite feel like we have control of time. The galaxy just does its thing now, right? Don’t forget, though, we have a handle on a controller. Let’s make use of it.² + +## Making use of an AnimationController + +Allowing anyone to control the galaxy seems a bit too permissive though, so I’m going to make it an easter egg. I’ll add a sibling to the galaxy that’s a simple button, hidden off in the corner, and I’ll pass it a reference to our controller, so that within its `onTap` listener, we can stop or restart the animation. + +```dart +Stack( + children: <Widget>[ + Align( + alignment: Alignment.bottomLeft, + child: TimeStopper( + controller: _animationController, + ), + ), + Align( + alignment: Alignment.center, + child: RotationTransition( + child: GalaxyFitz(), + alignment: Alignment.center, + turns: _animationController, + ), + ), + ], +) +``` + +The controller maintains — among other things — the status of the animation, which we can check and stop if we’re running or restart if we’re not. And, there you go! By using an animation controller, we’re able to control the animation on demand. But that’s not all you can do with the controller. + +```dart +class TimeStopper extends StatelessWidget { + final AnimationController controller; + + const TimeStopper({Key key, this.controller}) : super(key: key); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + if (controller.isAnimating) { + controller.stop(); + } else { + controller.repeat(); + } + }, + child: InvisibleBox(size: 100), + ); + } +} +``` + +With it, you can also animate to (or backwards from) a specific value, fling the animation forward with a given velocity, or control multiple animations with the same controller. + +<DashImage figure src="images/0UjrF-d2-BWLQyfEo.webp" alt="*Keeping your galaxy clean of unwanted rockets.*" caption="*Keeping your galaxy clean of unwanted rockets.*" /> + + +This was just our first taste of explicit animations in Flutter. We saw how a Transition widget works with `AnimationController`, to provide some directionality and control over how our animation works. In future posts, we’ll be diving deeper into explicit animations and how to get even more customized. + +<DashImage figure src="images/0eSjSmRfA6LMzx_Yc.webp" alt="*When the galaxy stops, everything stops*" caption="*When the galaxy stops, everything stops*" /> + + +1. *Galactic ticks are hard to hear, but AnimationController and TickerProviders help.* + +1. *As with all of your time controllers: use responsibly.* + +Articles in this series: + +* [How to Choose Which Flutter Animation Widget is Right for You?](https://medium.com/p/79ecfb7e72b5/edit) + +* [Flutter animation basics with implicit animations](https://medium.com/flutter/flutter-animation-basics-with-implicit-animations-95db481c5916) + +* [Custom Implicit Animations in Flutter…with TweenAnimationBuilder](https://medium.com/flutter/custom-implicit-animations-in-flutter-with-tweenanimationbuilder-c76540b47185) + +* [Directional animations with built-in explicit animations](https://medium.com/flutter/directional-animations-with-built-in-explicit-animations-3e7c5e6fbbd7) (this article) + +* [When should I useAnimatedBuilder or AnimatedWidget?](https://medium.com/flutter/when-should-i-useanimatedbuilder-or-animatedwidget-57ecae0959e8) + +* [Animation deep dive](https://medium.com/flutter/animation-deep-dive-39d3ffea111f) \ No newline at end of file diff --git a/sites/www/content/blog/do-flutter-apps-dream-of-platform-aware-widgets/images/0R1JRCwpVruJCSxJ_.jpg b/sites/www/content/blog/do-flutter-apps-dream-of-platform-aware-widgets/images/0R1JRCwpVruJCSxJ_.jpg new file mode 100644 index 0000000000..436596f48e Binary files /dev/null and b/sites/www/content/blog/do-flutter-apps-dream-of-platform-aware-widgets/images/0R1JRCwpVruJCSxJ_.jpg differ diff --git a/sites/www/content/blog/do-flutter-apps-dream-of-platform-aware-widgets/images/1xgjvZHIs2PUHXRrn8COFRw.webp b/sites/www/content/blog/do-flutter-apps-dream-of-platform-aware-widgets/images/1xgjvZHIs2PUHXRrn8COFRw.webp new file mode 100644 index 0000000000..cd4ea1786d Binary files /dev/null and b/sites/www/content/blog/do-flutter-apps-dream-of-platform-aware-widgets/images/1xgjvZHIs2PUHXRrn8COFRw.webp differ diff --git a/sites/www/content/blog/do-flutter-apps-dream-of-platform-aware-widgets/index.md b/sites/www/content/blog/do-flutter-apps-dream-of-platform-aware-widgets/index.md new file mode 100644 index 0000000000..ee632c0b8d --- /dev/null +++ b/sites/www/content/blog/do-flutter-apps-dream-of-platform-aware-widgets/index.md @@ -0,0 +1,140 @@ +--- +title: "Do Flutter apps dream of platform aware widgets?" +description: "Agnostic layout with platform specific widgets" +publishDate: 2018-03-01 +author: swavkulinski +image: images/1xgjvZHIs2PUHXRrn8COFRw.webp +category: tutorial +layout: blog +--- + +Flutter is a new cross-platform app development framework which promises absolute freedom in creating user experiences regardless of the host platform. It achieves that by taking over the rendering pipeline and managing everything by itself, from laying out the components, to drawing them. Customising everything is the lifeblood of Flutter. Applications which use a heavy design branding language are an obvious target for this platform independence. Think of Netflix, it feels and looks the same regardless if it runs on your iPhone, Android or PlayStation. + +But what about brands who would like to keep a platform-specific look and feel? Many companies embrace Material Design or iOS Human Interface language to keep their customer base in a familiar environment. + +Flutter is equipped with packages which contain a healthy set of native widgets for iOS and Android called **Cupertino** and **Material**. + +<DashImage figure src="images/1xgjvZHIs2PUHXRrn8COFRw.webp" alt="Cupertino and Material widgets in Flutter" caption="Cupertino and Material widgets in Flutter" /> + + +The framework is agnostic in its essence when it comes to UI. However, some widgets require an ancestor which belongs to the same “platform-specific” library, e.g. **RaisedButton** belongs to the Material package library and it requires a **Material** widget to be one of its ancestors. This presents developers with a problem in terms of reusability of the layout code. + +One of the questions I get a lot from the business is — *“but can we effectively build native-looking apps and still have reusable code?”* + +This is a valid question if we are looking for that native look and feel. My reaction to a question like that would be — yes, we can build two “native” layout trees, one for each host platform. Simple enough, right? + +Well, not really. We are fooling ourselves into two implementations, exactly the opposite of what we aimed to achieve. Both trees are inherently different as both platforms have different needs in terms of dependencies and initialisation. Our holy grail of reusable code fades away. + +I would like to propose an approach which allows building abstract UI and adjusts its look and behaviour depending on which platform it runs on. + +Consider the following constructors for two widgets which provide a top app bar: + +```dart +CupertinoNavigationBar ({ + this.leading, + this.middle, +}) +``` + +and + +```dart +AppBar ({ + this.leading, + this.title +}) +``` + +Both of the above play the same role, providing a top application bar in a **Cupertino** and **Material** style, but still they require some different properties. We need a solution which will abstract the way we are instantiating specific types and provide implementations based on the host platform. We will resort to the old trick of **Factory method pattern.** + +```dart +import 'package:flutter/material.dart'; +import 'dart:io' show Platform; + +abstract class PlatformWidget<I extends Widget, A extends Widget> extends StatelessWidget { + + @override + Widget build(BuildContext context) { + if(Platform.isAndroid) { + return createAndroidWidget(context); + } else if (Platform.isIOS) { + return createIosWidget(context); + } + // platform not supported returns an empty widget + return new Container(); + } + + I createIosWidget(BuildContext context); + + A createAndroidWidget(BuildContext context); + +} +``` + +In essence it is a *Platform-specific Widget Factory* using the framework’s **StatelessWidget** as an interface. + +When implemented, the above class can provide a custom constructor (or a number of named constructors) which supports the needs of both concrete classes. +> Note: The reason why I’ve chosen generics to return specific concrete classes is that sometimes the parent widget will need a specific type to be returned from the child widget. + +Having this out of the way, we can implement our first platform aware widget. + +```dart +class PlatformAppBar extends PlatformWidget<CupertinoNavigationBar, AppBar> { + + final Widget leading; + final Widget title; + + PlatformWidget({ + this.leading, + this.title, + }); + + @Override + CupertinoNavigationBar createIosWidget(BuildContext context) => new CupertinoNavigationBar ( + leading: leading, + middle: title, + ); + + @Override + AppBar createAndroidWidget(BuildContext context) => + new AppBar( + leading: leading, + title: title, + ) + ); +} +``` + +Pretty simple, right?. Notice that we have full control on the contents of the application bar widgets and they way we instantiate them. + +For the sake of time, let’s assume we’ve implemented a scaffold and a button as well. + +```dart +class PlatformScaffoldWidget extends PlatformWidget<CupertinoPageScaffold,Scaffold> { + ... + } + +class PlatformButton extends PlatformWidget<CupertinoButton,FlatButton> { + ... +} +``` + +Now we are ready to use (and reuse) our platform aware widgets. + +```dart +Widget build(BuildContext context) => + new PlatformScaffoldWidget( + appBar : new PlatformAppBarWidget( + leading: new PlatformButton ( + child: new Icon(Icons.ic_arrow_back), + onClick: () => _handleBack() + ), + title: new Text ("I love my Platform"), + ), + content: ... + ); +``` + +And we’re done! The above code will render a native looking app bar on both platforms, and our PlatformScaffoldWidget is ready to be reused throughout the rest of the app without any hassle. Have a look and let me know what you think + +<GitHubEmbed repo="swavkulinski/flutter-platform-specific-widgets" title="flutter-platform-specific-widgets - Purpose of this project is to create extendable architecture of making platform…" image="images/0R1JRCwpVruJCSxJ_.jpg" /> diff --git a/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0CmgeijL3JkkAQA1_.webp b/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0CmgeijL3JkkAQA1_.webp new file mode 100644 index 0000000000..3aabb4bc8d Binary files /dev/null and b/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0CmgeijL3JkkAQA1_.webp differ diff --git a/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0EHUt9u95L4cLrRBk.webp b/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0EHUt9u95L4cLrRBk.webp new file mode 100644 index 0000000000..c826a4ad33 Binary files /dev/null and b/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0EHUt9u95L4cLrRBk.webp differ diff --git a/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0EMCuq3u9Ou_ALwgz.webp b/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0EMCuq3u9Ou_ALwgz.webp new file mode 100644 index 0000000000..214bf0ccde Binary files /dev/null and b/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0EMCuq3u9Ou_ALwgz.webp differ diff --git a/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0F-sHYKccRgOOQ85L.webp b/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0F-sHYKccRgOOQ85L.webp new file mode 100644 index 0000000000..4c9bbaecd5 Binary files /dev/null and b/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0F-sHYKccRgOOQ85L.webp differ diff --git a/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0JMOa0ZRVpHAy7prt.webp b/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0JMOa0ZRVpHAy7prt.webp new file mode 100644 index 0000000000..01f3fd44ad Binary files /dev/null and b/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0JMOa0ZRVpHAy7prt.webp differ diff --git a/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0Jv2irMQ8bSqUxGTW.webp b/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0Jv2irMQ8bSqUxGTW.webp new file mode 100644 index 0000000000..771f1e9d90 Binary files /dev/null and b/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0Jv2irMQ8bSqUxGTW.webp differ diff --git a/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0PihM7mrr3InFiCOZ.webp b/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0PihM7mrr3InFiCOZ.webp new file mode 100644 index 0000000000..daca625d05 Binary files /dev/null and b/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0PihM7mrr3InFiCOZ.webp differ diff --git a/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0hjsh42RwSy9QAK1S.webp b/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0hjsh42RwSy9QAK1S.webp new file mode 100644 index 0000000000..c323514895 Binary files /dev/null and b/sites/www/content/blog/does-flutter-boost-developer-productivity/images/0hjsh42RwSy9QAK1S.webp differ diff --git a/sites/www/content/blog/does-flutter-boost-developer-productivity/index.md b/sites/www/content/blog/does-flutter-boost-developer-productivity/index.md new file mode 100644 index 0000000000..c1b0fe1922 --- /dev/null +++ b/sites/www/content/blog/does-flutter-boost-developer-productivity/index.md @@ -0,0 +1,89 @@ +--- +title: "Does Flutter boost developer productivity?" +description: "The Flutter team continues to run a quarterly user survey to hear from you, our Flutter developers. The first survey in 2022 ran between…" +publishDate: 2022-05-05 +author: jayoung-lee +image: images/0hjsh42RwSy9QAK1S.webp +category: announcements +layout: blog +--- + +The Flutter team continues to run a quarterly user survey to hear from you, our Flutter developers. The first survey in 2022 ran between February 22nd and March 3rd, and was accessed through Flutter IDE plugins, the docs.flutter.dev website, and Twitter — and over eight thousand of you responded. + +Flutter continues to be loved by most developers (93% positively satisfied), and even scored the highest percentage of “very satisfied” (60%) in its history! This is noteworthy because the number of Flutter developers grew by over 10% since the last quarterly survey (from November 2021 to February 2022). We’re happy to see that Flutter is maintaining a high level of satisfaction even as the community grows. + +<DashImage figure src="images/0hjsh42RwSy9QAK1S.webp" alt="Satisfaction with Flutter over time" caption="Satisfaction with Flutter over time" /> + + +We focused on the following four topics in this survey: + +* **IDE plugin:** What can we do to improve Flutter’s IDE plugin support? + +* **Dart scalability:** Do Dart tools grow slow or unresponsive as a Flutter project grows larger? + +* **Game development:** Are you interested in game development? *— (More about this in an upcoming blog post.)* + +* **Flutter’s value to developers:** Is Flutter delivering the values promised to developers? + +We don’t take your support for granted and we’re keeping an eye out for signals that show where we can further improve. We recognize that there is always room for improvement, and in this article we’ll tell you more about what we’ve learned and how that info drives our roadmap. + +## IDE plugins + +According to this survey, 63.5% of all developers were “very” satisfied with Flutter’s IDE plugins. What about the rest of the population? Why are they not fully satisfied? + +As shown in the following figure, there was no one item that stood out from others as the reason for not being fully satisfied with the IDE plugins. Missing features (35%) was the biggest reason for both Android Studio/IntelliJ and VS Code users, but slow plugin (29%), memory consumption (28%), buggy plugin (26%), all received a fair number of votes, too. (The IDE type is logged when a respondent clicks on the survey link within their IDE.) + +<DashImage figure src="images/0EMCuq3u9Ou_ALwgz.webp" alt="Reasons for not being fully satisfied with Flutter’s IDE plugins" caption="Reasons for not being fully satisfied with Flutter’s IDE plugins" /> + + +“IntelliJ” label includes Android Studio users as well. + +When we asked what features are missing or inadequately supported, refactoring and quick fixes were chosen as the top two that need better support. This was somewhat surprising, because we didn’t expect that refactoring would be the highest ranked. Also, we were surprised to learn that Android Studio/IntelliJ users are very concerned about navigation, though we do understand that Android Studio/IntelliJ users care more about Android integration than VS Code users. There’s also great interest from developers for a UI builder. + +<DashImage figure src="images/0JMOa0ZRVpHAy7prt.webp" alt="Missing or inadequately supported features in the IDE" caption="Missing or inadequately supported features in the IDE" /> + + +In summary, we now feel better aligned with Flutter developers. Still, we will continue to learn more about what’s needed to improve areas of concern through various studies. + +## Dart tools + +Dart tools, such as Dart analyzer, compiler, DevTools, and IDE plugins, make it easy to use the language. We heard from a few developers that Dart tools can become slow or even unresponsive when the Flutter project is large. We want to assess how widespread and severe this problem is in our next survey. + +From this survey we found that about a quarter of the developers have experienced this symptom (24.5%), and almost a half of them said that it has had an extremely negative (16.7%) or moderately negative (29.6%) impact on development. + +<DashImage figure src="images/0Jv2irMQ8bSqUxGTW.webp" alt="Developers encountered a Dart tool growing slow or unresponsive as their Flutter project grew larger" caption="Developers encountered a Dart tool growing slow or unresponsive as their Flutter project grew larger" /> + + +<DashImage figure src="images/0EHUt9u95L4cLrRBk.webp" alt="Impact of a slow or unresponsive Dart tool on productivity" caption="Impact of a slow or unresponsive Dart tool on productivity" /> + + +We expected these numbers to be lower and we’re eager to improve in this area, so we’re planning to reach out to some developers who expressed interest in a follow-up conversation to reproduce the problem and eventually improve the performance of the tools. + +## Flutter’s value to developers + +Flutter promises to provide a framework for building beautiful, fast, multi-platform applications from a single codebase. In this survey, we explored whether Flutter developers feel that we’ve met these values. + +We asked different questions to those who have converted existing apps to Flutter and to those who created a new Flutter app from scratch. With multiple answers allowed, 59.2% have created a new app from scratch, and 26.5% either converted an existing app or added Flutter components to an existing app. + +<DashImage figure src="images/0CmgeijL3JkkAQA1_.webp" alt="Developers’ experience developing and publishing Flutter apps" caption="Developers’ experience developing and publishing Flutter apps" /> + + +To those who created a new app with Flutter, we asked to what extent they agree with three statements about integration with other Google services, development productivity, and target platforms. We found that over 90% of them agreed to “Flutter reduced time to build and publish new apps, compared to other technologies that I have used” (92.1%) and to “Flutter enabled my app to target more platforms”. (92.2%) + +<DashImage figure src="images/0F-sHYKccRgOOQ85L.webp" alt="The impact of creating a new app with Flutter" caption="The impact of creating a new app with Flutter" /> + + +Similar questions were asked of those who adopted Flutter for existing apps. 90.7% agreed that “Flutter enabled my app to be developed more quickly than before” and 85.3% agreed that “Flutter enabled my app to be available on more platforms than before”. We also found that 84.8% of the developers agreed that “Flutter made my app look more beautiful than before”. + +<DashImage figure src="images/0PihM7mrr3InFiCOZ.webp" alt="The impact of adopting Flutter for an existing app" caption="The impact of adopting Flutter for an existing app" /> + + +Of course we heard what Flutter can do better, such as providing more plugins for all supported platforms, adding more documentation, improving perceived performance, integrating better with other tools, and so on. Some comments also mentioned that certain problem areas haven’t changed after years of surveys. While we strive to address the issues and improve the product, we appreciate your patience! Nevertheless, we are pleased to learn and report that our developers are targeting more platforms in a more productive way. + +## Wrapping up + +Special thanks go to everyone who participated in our survey! The Flutter team members dedicate time every quarter to analyze and digest the feedback and make decisions for the next quarter and beyond. We’ll continue to reach out to hear what we can do to make your experience with Flutter more enjoyable and more productive. + +Flutter’s UX research team is also running a variety of user experience studies. If you are interested in participating, please [sign up](https://docs.google.com/forms/d/e/1FAIpQLSe0i4De809KXVCdljGKrjMj3lxhuzbuFKCtY5PEQPCYtGxFMg/viewform) to be considered for a future study. + +And please continue to participate and share your thoughts. See you in the next survey! \ No newline at end of file diff --git a/sites/www/content/blog/enums-with-extensions-dart/images/1j9632GFfT3IT7sfeiHUZtw.webp b/sites/www/content/blog/enums-with-extensions-dart/images/1j9632GFfT3IT7sfeiHUZtw.webp new file mode 100644 index 0000000000..4cc3e5700c Binary files /dev/null and b/sites/www/content/blog/enums-with-extensions-dart/images/1j9632GFfT3IT7sfeiHUZtw.webp differ diff --git a/sites/www/content/blog/enums-with-extensions-dart/index.md b/sites/www/content/blog/enums-with-extensions-dart/index.md new file mode 100644 index 0000000000..fd3f70341d --- /dev/null +++ b/sites/www/content/blog/enums-with-extensions-dart/index.md @@ -0,0 +1,142 @@ +--- +title: "Enums with Extensions Dart" +description: "About the author: Jose recently graduated from university and now works on Material, a design system that helps teams build high-quality…" +publishDate: 2020-07-17 +author: josealba +image: images/1j9632GFfT3IT7sfeiHUZtw.webp +category: spotlight +layout: blog +--- + +## Enums with Extensions in Dart + +How to write cleaner code using extension methods in Dart + +<DashImage figure src="images/1j9632GFfT3IT7sfeiHUZtw.webp" alt="Example of Dart code using an extension method" caption="Example of Dart code using an extension method" /> + + +Dart has recently released support for extension methods, which means that you can now use this awesome feature. This article describes one example of how I use extension methods in my Flutter code. + +I’ve always used enums in my Flutter code, but integrating enums and extension methods has made developing in Flutter easier and the code cleaner to read. + +Let’s say you make an enumeration and your text is dependent on your enum. Previously, I would have used a switch statement within the `Widget` by using [IIFE](https://en.wikipedia.org/wiki/Immediately_invoked_function_expression) ([medium post](https://medium.com/dartlang/3-cool-dart-patterns-6d8d9d3d8fb8)). An *immediately invoked function expression* lets you invoke an anonymous method, which is a useful Dart pattern. But using this pattern might lead to spaghetti code, as shown in the example below. Also, if you need to add the same text elsewhere, you would have to copy the entire code snippet instead of just making a function call. + +``` +Text((){ + + switch (selectedColor) { + + case SelectedColor.PrimaryColor: + + return 'This is the Primary Color'; + + case SelectedColor.SecondaryColor: + + return 'This is the Secondary Color'; + + default: + + return 'SelectedScheme Title is null'; + + } + +}()), + +Text((){ + + switch (selectedColor) { + + case SelectedColor.PrimaryColor: + + return 'This is the Primary Color'; + + case SelectedColor.SecondaryColor: + + return 'This is the Secondary Color'; + + default: + + return 'SelectedScheme Title is null'; + + } + +}()), +``` + + +Also, what happens if you need to change the text color based on the enum? You would need to use another IIFE to get the `PrimaryColor` or `SecondaryColor`. + +Instead, you can extend your enum and create a method within that extension so you don’t have to use an IIFE . That way when you invoke your method in your code, it looks cleaner but still gives you the same result. This is an important technique that Java developers are familiar with, and is an expected capability of enums. + +``` +enum SelectedColor { + + primaryColor, + + secondaryColor, + +} + +extension SelectedColorExtension on SelectedColor { + + String get name => describeEnum(this); + + String get displayTitle { + + switch (this) { + + case SelectedColor.PrimaryColor: + + return 'This is the Primary Color'; + + case SelectedColor.SecondaryColor: + + return 'This is the Secondary Color'; + + default: + + return 'SelectedScheme Title is null'; + + } + + } + +} +``` + + +The `describeEnum()` function, which is defined in the Flutter foundation library, strips off the enum class name from `enumEntry.toString()`. I provide the code below in the event that you want to use it outside of Flutter development, for other Dart projects: + +``` +String describeEnum(Object enumEntry) { + + final String description = enumEntry.toString(); + + final int indexOfDot = description.indexOf('.'); + + assert(indexOfDot != -1 && indexOfDot < description.length - 1); + + return description.substring(indexOfDot + 1); + +} +``` + + +To play around with the example mentioned above try this Pen. + +<iframe src="https://codepen.io/JoseAlba/embed/zYvLPBM" width="800" height="600" frameborder="0" allowfullscreen></iframe> + + +In the full example, which you can see in CodePen above, the enum is extended with four methods: `displayTitle()`, `displayColorChangeText()`, `color()`, and `getRandomSelectedColor()`. When calling the `displayTitle()` method within the Flutter code you can see that the code is modularized, making it cleaner, as compared to the IIFE. Additionally, if you ever decide to add another enum variable within your enumeration it’s easier to update since all of the code is within the extension. This is seen when the `color()` method was called to change the text color. If I wanted to do this with the IIFE the code would get complicated quickly. It’s also easier to add methods in the future by using extensions; you can see this when the `color()` method was invoked in the CodePen. + +The [Flutter Gallery](https://gallery.flutter.dev/#/) uses enums with extensions when [handling category demos](https://github.com/flutter/gallery/blob/74d9d92883c6ca10742c44df309f065289cf2f55/lib/data/demos.dart#L53-L75). The Gallery uses enums to categorize the demo code and uses extensions methods when they want to convert their enumeration into text for the application. + +## Closing remarks + +[Extension methods](https://dart.dev/guides/language/extension-methods) are a powerful tool provided by Dart. By using enums and extension methods together, you can have cleaner, more scalable code. + +Learn more about the useful dart patterns in[ 3 Cool Dart Patterns for everyday programming in Flutter.](https://medium.com/dartlang/3-cool-dart-patterns-6d8d9d3d8fb8) + +*About the author: Jose recently graduated from university and now works on [Material](https://material.io), a design system that helps teams build high-quality digital experiences. Jose’s team maintains the Flutter [material library](https://api.flutter.dev/flutter/material/material-library.html).* + +*To learn more about Jose, visit his pages on [GitHub](https://github.com/JoseAlba), [LinkedIn](https://www.linkedin.com/in/josealba1996/), [YouTube](https://www.youtube.com/channel/UCOdKA_On0oPe1tz02z1QfxA?view_as=subscriber), and [Instagram](https://www.instagram.com/jose.alba/).* \ No newline at end of file diff --git a/sites/www/content/blog/event-loop-in-widget-tester/images/1ajiMVoQuEXYZ4IJzr-yOQQ.webp b/sites/www/content/blog/event-loop-in-widget-tester/images/1ajiMVoQuEXYZ4IJzr-yOQQ.webp new file mode 100644 index 0000000000..286d4d7cf1 Binary files /dev/null and b/sites/www/content/blog/event-loop-in-widget-tester/images/1ajiMVoQuEXYZ4IJzr-yOQQ.webp differ diff --git a/sites/www/content/blog/event-loop-in-widget-tester/images/1hf94gBylm_JwULMwfWFncw.webp b/sites/www/content/blog/event-loop-in-widget-tester/images/1hf94gBylm_JwULMwfWFncw.webp new file mode 100644 index 0000000000..fd7ee2cdcd Binary files /dev/null and b/sites/www/content/blog/event-loop-in-widget-tester/images/1hf94gBylm_JwULMwfWFncw.webp differ diff --git a/sites/www/content/blog/event-loop-in-widget-tester/index.md b/sites/www/content/blog/event-loop-in-widget-tester/index.md new file mode 100644 index 0000000000..17658cfcb4 --- /dev/null +++ b/sites/www/content/blog/event-loop-in-widget-tester/index.md @@ -0,0 +1,115 @@ +--- +title: "Event Loop in Widget Tester" +description: "Flutter’s widget tester is a great way to do unit testing on widgets. It allows you to bring up a headless environment where you can…" +publishDate: 2018-11-14 +author: mehmetf +image: images/1ajiMVoQuEXYZ4IJzr-yOQQ.webp +category: announcements +layout: blog +--- + +Flutter’s widget tester is a great way to perform unit testing on widgets. It allows you to bring up a headless environment where you can assert properties of your widgets, simulate gestures such as tap or fling and easily test their response. + +This article takes a closer look at how widget tests manage the [event loop](https://webdev.dartlang.org/articles/performance/event-loop#darts-event-loop-and-queues) and what that means for asynchronous tasks. + +## Pump + +During my tenure as a Flutter app developer, I have found that most developers do not fully understand what [pump()](https://docs.flutter.io/flutter/flutter_test/TestWidgetsFlutterBinding/pump.html) does other than the fact that they need to call it to make tests pass. + +Documentation states that: +> Pump triggers a frame sequence (build/layout/paint/etc), then flushes microtasks. + +A microtask is anything that is scheduled for execution in the event loop. Calling pump() repeatedly flushes microtasks and allows new ones to be created. + +Say your widget depends on some data store that returns a Future to be resolved. In tests, you typically have a fake store that returns data immediately. That does not change the fact that a Future still is a microtask that needs to be resolved. You will need to call pump to flush these tasks and trigger the build so your widget can be tested. + +Without pump, the event flow looks like this: + +<DashImage figure src="images/1ajiMVoQuEXYZ4IJzr-yOQQ.webp" alt="Tester flow without pump" caption="Tester flow without pump" /> + + +With pump, you are able to rebuild the widget and the test passes: + +<DashImage figure src="images/1hf94gBylm_JwULMwfWFncw.webp" /> + + +You might think calling pump advances the clock because that’s how your widget works in real life. You initiate a data fetch, some time later the Future resolves and your widget displays the data. In reality, pump does not advance the clock. For instance, an animation that has a preset duration will *never* finish just by calling pump. + +To solve this problem, you will have to call pump with a *duration*. It flushes microtasks, schedules a frame but also advances the clock by the specified duration. It effectively schedules a new frame, then pretends that all frames were missed until the duration has expired, as if there the system had been too busy to schedule the app for that time. This allows you to jump past animations quickly and is essentially how the *pumpAndSettle* API **is implemented. + +## FakeAsync + +Pump can skip ahead this way because WidgetTester runs in the *FakeAsync zone*. This zone creates a predictable and controllable environment for tests to run. The flip side is that Futures (and async work in general) behave very differently in this zone compared to your real app. + +Consider this example: + +```dart +import "dart:async"; +import "package:flutter_test/flutter_test.dart"; + +void main() { + Future future; + + setUp(() { + future = new Future.value(); + }); + + testWidgets("awaiting future with value from setUp works", (WidgetTester tester) async { + await future; + }); +} +``` + +This test never finishes because nobody is calling *pump(Duration)* to advance the time on the awaited future on line 12. + +The solution is to use a function called [runAsync](https://docs.flutter.io/flutter/flutter_test/WidgetTester/runAsync.html) to make the async work run outside of the fake zone, which decouples it from the fake test-controlled time flow and lets it run in *real time*. + +```dart +import "dart:async"; +import "package:flutter_test/flutter_test.dart"; + +void main() { + Future future; + + setUp(() { + future = new Future.value(); + }); + + testWidgets("awaiting future with value from setUp works", (WidgetTester tester) async { + await tester.runAsync(() => future); + }); +} +``` + +While this is a straight-forward example, it becomes more subtle when the application (or even framework) code starts doing async work. + +Let’s go over two more examples. + +### Snackbar + +Imagine that you have written a widget that shows a snackbar with a cancel button when an async operation is ongoing. The user can tap on the cancel button and interrupt the operation. You want to write a test for the cancel button and it should be trivial: + +* Create the snackbar widget with some async work + +* Simulate the tap on the button + +* Check the widget’s state + +You might be tempted to code this by defining a *Future.delayed()* for your async work. You soon discover that the test is not working as expected. You see that while the tap simulation on the button is being executed, the tap handler in your widget is never called. + +The problem is that Future.delayed() is running inside the fake async zone and waiting for someone to advance the time. Tap events cannot be consumed because the frames are not advancing past the previous operation. + +The solution, once again, is to put the work in runAsync so that it can run in real time, let the button consume the tap and trigger the flow user wants to test. +> runAsync makes your async work run in *real time*. If you put a Future.delayed(5 seconds) in there, then your actual test time will be prolonged by 5 ***very real*** seconds. + +### Method Channel + +Sometimes, FakeAsync bites in even subtler ways. If a framework call awaits on a platform channel, this await cannot be completed even if the native side is trying to send data because the tests run in a fake zone. Unfortunately there is no solution on the application side for this. The framework controlled channels need to be explicitly tracked by the test framework and advanced manually so they can finish. So please file an issue with Flutter if you run into this scenario as we would love to learn more. + +## To sum up… + +Keep these points in mind: + +* If your widget tests involve async operations, any real async work in tests needs to be done in runAsync *including bogus Future.delayed()* constructs. + +* Any real async work performed in your widgets *cannot* be tested via widget tests. You need to inject an overridable service so that you can either take over the execution of the async task in the test or provide a synchronous implementation. \ No newline at end of file diff --git a/sites/www/content/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing/images/074sQ7Fl7ueommzJy.webp b/sites/www/content/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing/images/074sQ7Fl7ueommzJy.webp new file mode 100644 index 0000000000..04040097f0 Binary files /dev/null and b/sites/www/content/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing/images/074sQ7Fl7ueommzJy.webp differ diff --git a/sites/www/content/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing/images/0D6jZA_ksvRN5EJi1.webp b/sites/www/content/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing/images/0D6jZA_ksvRN5EJi1.webp new file mode 100644 index 0000000000..decc0f331a Binary files /dev/null and b/sites/www/content/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing/images/0D6jZA_ksvRN5EJi1.webp differ diff --git a/sites/www/content/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing/images/0ae_YppUn96m93qkT.webp b/sites/www/content/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing/images/0ae_YppUn96m93qkT.webp new file mode 100644 index 0000000000..e8a6b17f80 Binary files /dev/null and b/sites/www/content/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing/images/0ae_YppUn96m93qkT.webp differ diff --git a/sites/www/content/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing/images/0at7FCzjM5lAd8NC_.webp b/sites/www/content/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing/images/0at7FCzjM5lAd8NC_.webp new file mode 100644 index 0000000000..74e725f337 Binary files /dev/null and b/sites/www/content/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing/images/0at7FCzjM5lAd8NC_.webp differ diff --git a/sites/www/content/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing/images/0ilMs7WWcpN3q9UxO.webp b/sites/www/content/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing/images/0ilMs7WWcpN3q9UxO.webp new file mode 100644 index 0000000000..0ef54c920e Binary files /dev/null and b/sites/www/content/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing/images/0ilMs7WWcpN3q9UxO.webp differ diff --git a/sites/www/content/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing/index.md b/sites/www/content/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing/index.md new file mode 100644 index 0000000000..b2f62a47d5 --- /dev/null +++ b/sites/www/content/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing/index.md @@ -0,0 +1,934 @@ +--- +title: "Executing Dart in the Background with Flutter Plugins and Geofencing" +description: "04/11/2022: This article has been updated to replace broken links due to docs and plugins changing names or locations." +publishDate: 2018-09-20 +author: bkonyi +image: images/0ae_YppUn96m93qkT.webp +category: tutorial +layout: blog +--- + +<DashImage figure src="images/0ae_YppUn96m93qkT.webp" alt="No garage door remote? Not a problem with Flutter and a Raspberry Pi!" caption="No garage door remote? Not a problem with Flutter and a Raspberry Pi!" /> + + +***04/11/2022***: *This article has been updated to replace broken links due to docs and plugins changing names or locations.* + +***09/10/2020***: *This article has been updated to reflect the move to the v2 Android embedding API. Migration instructions for applications created pre-1.12 can be found [here](https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects), and [here](https://flutter.dev/docs/development/packages-and-plugins/plugin-api-migration) for plugins.* + +Whether handling push notifications, location updates, or sensor events, many useful features require that an application has the ability to handle events without user interaction, even when not running in the foreground. Up until this point, applications written using Flutter could only handle background events using platform code, and plugins had no way of allowing for plugin users to provide a callback to handle background events in Dart. Basically, if a Flutter user wants to handle background events in an application, they were required to create a platform-specific implementation for each of their target platforms. + +Luckily, this is no longer the situation, thanks to the arrival of Flutter support for background execution of Dart code. Having designed much of the Flutter background execution flow, I’m excited to share my experiences developing plugins which take advantage of this functionality, such as the [android_alarm_manager](https://pub.dartlang.org/packages/android_alarm_manager#-readme-tab-) and [an iOS location event handler](https://github.com/flutter/plugins/tree/master/packages/location_background), to help you get started creating your own plugins. + +Throughout the rest of this article, I’ll explore (in detail) the process of building a Flutter plugin that handles background geofencing events on both Android and iOS. Finally, just for fun, I’ll showcase how I used this [geofencing plugin](https://github.com/bkonyi/FlutterGeofencing) to create a simple application that can be used to open my garage door automatically when I get close to home. + +### Table of Contents + +* [Geofencing: defining the Dart API](https://medium.com/p/2b3e40a1a124#56b7) + +* [Dart background execution](https://medium.com/p/2b3e40a1a124#155c) + +* [Background execution: Android (Kotlin)](https://medium.com/p/2b3e40a1a124#1b99) + +* [Background execution: iOS (Objective-C)](https://medium.com/p/2b3e40a1a124#5979) + +* [Usage example: operating a garage door with geofencing](https://medium.com/p/2b3e40a1a124#6db8) + +* [Conclusion](https://medium.com/p/2b3e40a1a124#3d77) + +* [Resources](https://medium.com/p/2b3e40a1a124#2bc9) + +## Geofencing: defining the Dart API + +Before writing any platform-specific code, I’ll first need to define the Dart API for the geofencing plugin. Since [Android](https://developer.android.com/training/location/geofencing) and [iOS](https://developer.apple.com/documentation/corelocation/monitoring_the_user_s_proximity_to_geographic_regions?language=objc) each have their own APIs for registering and handling geofencing events, I want the Dart interface to provide a reasonable geofencing abstraction that is compatible with both platforms. Without going into too much detail about Android and iOS APIs, the following roughly describes the Dart interface that I’ll use for my plugin: + +```dart +/// A circular region which represents a geofence. +abstract class GeofenceRegion { + /// The ID associated with the geofence. + /// + /// This ID identifies the geofence and is required to delete a + /// specific geofence. + final String id; + + /// The location (center point) of the geofence. + final Location location; + + /// The radius around `location` that is part of the geofence. + final double radius; + + /// Listen to these geofence events. + final List<GeofenceEvent> triggers; + + /// Android-specific settings for a geofence. + final AndroidGeofencingSettings androidSettings; + + GeofenceRegion( + this.id, double latitude, double longitude, this.radius, this.triggers, + {AndroidGeofencingSettings androidSettings}); +} + +abstract class GeofencingPlugin { + /// Initialize the plugin and request relevant permissions from the user. + static Future<bool> initialize() async; + + /// Register for geofence events for a [GeofenceRegion]. + /// + /// `region` is the geofence region to register with the system. + /// `callback` is the method to be called when a geofence event associated + /// with `region` occurs. + static Future<bool> registerGeofence( + GeofenceRegion region, + void Function(List<String> id, Location location, GeofenceEvent event) callback); + + /// Stop receiving geofence events for a given [GeofenceRegion]. + static Future<bool> removeGeofence(GeofenceRegion region); + + /// Stop receiving geofence events for an identifier associated with a + /// geofence region. + static Future<bool> removeGeofenceById(String id) async; +} +``` + +This interface provides the following functionality to users of the plugin: + +* The ability to create instances of `GeofenceRegion`, which contain the coordinates and radius of a geofence, a unique ID, and a list of geofencing events to listen for. Since Android provides a richer set of options for defining geofences than iOS, Android-specific options are made available through the optional `androidSettings` property. + +* `GeofencingPlugin.registerGeofence` allows for the registration of a `GeofenceRegion` instance with a callback that is invoked when a geofence event for that region is received. + +* `GeofencingPlugin.removeGeofence` and `GeofencingPlugin.removeGeofenceById` unregister a `GeofenceRegion` from triggering additional events. + +Overall, this interface is rather simple and (mostly) platform agnostic, making the plugin easy to use on both Android and iOS. + +## Dart background execution + +This section covers how to set up your isolate for background execution. You will learn how to reference callbacks, and how to use the callback dispatcher. + +### Referencing Callbacks + +Now that the Dart interface defined, start adding plumbing to communicate with the platform-specific portions of the plugin. For example, the following code initializes the geofencing plugin and registers the geofences: + +```dart +abstract class GeofencingPlugin { + static const MethodChannel _channel = + const MethodChannel('plugins.flutter.io/geofencing_plugin'); + + static Future<bool> initialize() async { + final callback = PluginUtilities.getCallbackHandle(callbackDispatcher); + await _channel.invokeMethod('GeofencingPlugin.initializeService', + <dynamic>[callback.toRawHandle()]); + } + + static Future<bool> registerGeofence( + GeofenceRegion region, + void Function(List<String> id, Location location, GeofenceEvent event) + callback) { + if (Platform.isIOS && + region.triggers.contains(GeofenceEvent.dwell) && + (region.triggers.length == 1)) { + throw UnsupportedError("iOS does not support 'GeofenceEvent.dwell'"); + } + final args = <dynamic>[ + PluginUtilities.getCallbackHandle(callback).toRawHandle() + ]; + args.addAll(region._toArgs()); + _channel.invokeMethod('GeofencingPlugin.registerGeofence', args); + } + + /* + * … `removeGeofence` methods here … + */ +} + +``` + +If you’ve previously developed Flutter plugins and are familiar with `MethodChannel`, this should look as expected, for the most part. (If you’re new to plugin development, check out [the platform channels article](https://flutter.io/platform-channels/) for an introduction). However, the two calls to `PluginUtilities.getCallbackHandle` might stand out. + +<DashImage figure src="images/0ilMs7WWcpN3q9UxO.webp" alt="*Callback handles are managed by the Flutter engine and can be used to reference and lookup callbacks across isolates.*" caption="*Callback handles are managed by the Flutter engine and can be used to reference and lookup callbacks across isolates.*" /> + + +In order to invoke a Dart callback as a result of a background event, you must retrieve a handle that is passed between Dart and platform code while also allowing for lookup of the callback across platform threads and Dart [isolates](https://api.dartlang.org/stable/2.0.0/dart-isolate/dart-isolate-library.html). + +**Aside:** Retrieving a `CallbackHandle` for a method from `PluginUtilities.getCallbackHandle` has the side effect of populating a callback cache within the Flutter engine, as seen in the diagram above. This cache maps information required to retrieve callbacks to raw integer handles, which are simply hashes calculated based on the properties of the callback. This cache persists across launches, but be aware that ***callback lookups may fail if the callback is renamed or moved and `PluginUtilities.getCallbackHandle` is not called for the updated callback**.* + +In the code above, two instances of `CallbackHandle` are obtained: one for the callback, which is associated with a `GeofenceRegion`, and another for a method of the name `callbackDispatcher`. The `callbackDispatcher` method, the entrypoint of the background isolate, is responsible for preprocessing raw geofence event data, looking up callbacks via `PluginUtilities.getCallbackFromHandle`, and invoking them for registered geofences. + +### The Callback Dispatcher + +As mentioned at the end of the previous section, I’ll use a pattern that I refer to as the *callback dispatcher* to create the entrypoint for the geofencing plugin’s background isolate. This pattern allows for performing the initialization required to establish communication channels with platform code while also allowing for the creation of non-trivial interfaces for callback methods. For this geofencing plugin, the callback dispatcher implementation is as follows: + +```dart +void callbackDispatcher() { + // 1. Initialize MethodChannel used to communicate with the platform portion of the plugin. + const MethodChannel _backgroundChannel = + MethodChannel('plugins.flutter.io/geofencing_plugin_background'); + + // 2. Setup internal state needed for MethodChannels. + WidgetsFlutterBinding.ensureInitialized(); + + // 3. Listen for background events from the platform portion of the plugin. + _backgroundChannel.setMethodCallHandler((MethodCall call) async { + final args = call.arguments; + + // 3.1. Retrieve callback instance for handle. + final Function callback = PluginUtilities.getCallbackFromHandle( + CallbackHandle.fromRawHandle(args[0])); + assert(callback != null); + + // 3.2. Preprocess arguments. + final triggeringGeofences = args[1].cast<String>(); + final locationList = args[2].cast<double>(); + final triggeringLocation = locationFromList(locationList); + final GeofenceEvent event = intToGeofenceEvent(args[3]); + + // 3.3. Invoke callback. + callback(triggeringGeofences, triggeringLocation, event); + }); + + // 4. Alert plugin that the callback handler is ready for events. + _backgroundChannel.invokeMethod('GeofencingService.initialized'); +} + +``` + +As you can see, on the invocation of `callbackDispatcher` (upon the creation of the geofencing plugin’s background isolate), only four operations are performed. First, a `MethodChannel` is created for listening to events from the plugin. Next, `WidgetsFlutterBinding.ensureInitialized()` is called to initialize state needed to communicate with the Flutter engine. At this point, the `MethodCall` handler is set to process plugin events before finally alerting the platform portion of the plugin that the background isolate is initialized and ready to start handling events. + +Once the plugin starts sending events to the callback dispatcher, the callback provided by the plugin user can be invoked. First, `PluginUtilities.getCallbackFromHandle` is called to retrieve an instance of the callback associated with the triggered geofencing event using the raw callback handle. Next, the raw arguments from the `MethodCall` are refined into: + +* An instance of `List<String>` for the IDs of the geofences that were triggered + +* An instance of `Location` describing the current location of the device + +* An instance of the `GeofenceEvent` enum that represents whether the device has entered, exited, or dwelled within the triggered geofences. + +Then provide this info as arguments to our callback. + +**Important Note:** You may have noticed that no state is kept within the callback handler. This is because ***there is no guarantee that the background isolate will stay alive while the application itself is backgrounded***. Both Android and iOS have lifecycle policies that can result in background services or execution being killed, meaning that the background isolate may be destroyed and then recreated the next time the application is woken up. As a result, ***best practice avoids storing volatile state in either the callback handler or user-provided callbacks***. + +At this point, we now have all of the Dart code needed for the plugin! Now, onto the platform-specific portion of the geofencing plugin. + +## Background execution: Android (Kotlin) + +For the Android implementation of the plugin, I’ll need to implement the following classes: + +* The `GeofencingPlugin` class, which is registered with the Flutter engine in order to receive and handle method calls made from Dart code + +* A `GeofencingBroadcastReceiver`, which is invoked by the system on a geofence event + +* The `GeofencingService`, which creates the background isolate, initializes the callback dispatcher described earlier, and processes geofence events before invoking the callback dispatcher. + +This trinity of 1) plugin, 2) broadcast receiver, and 3) service classes is a common pattern for plugins on Android, so it is worth becoming familiar with it. Although I’ve decided to use Kotlin for this plugin, everything here can also be implemented using Java. + +### GeofencingPlugin + +As previously mentioned, the main purpose of the `GeofencingPlugin` is to process requests from Dart code and then register or remove geofences based on the contents of said request. An instance of this class is automatically created and added to the plugin registry at application startup. + +This class implements two interfaces that are required for basic functionality of the plugin: + +* [`FlutterPlugin`](https://api.flutter.dev/javadoc/io/flutter/embedding/engine/plugins/FlutterPlugin.html): declares `onAttachedToEngine` and `onDetachedFromEngine`, used to notify the plugin of its connection status to a Flutter engine instance. + +* [`MethodCallHandler`](https://api.flutter.dev/javadoc/io/flutter/plugin/common/MethodChannel.MethodCallHandler.html): declares `onMethodCall`, the method used to process messages sent to the plugin over a `MethodChannel`. + +Most plugins need to implement both `FlutterPlugin` and `MethodCallHandler`, but some plugins may also require information about the current `Activity` or other application component. + +To get access to application components currently attached to the plugin instance, a plugin should implement one or more of the “awareness” interfaces for [`Activity`](https://api.flutter.dev/javadoc/io/flutter/embedding/engine/plugins/activity/ActivityAware.html), [`BroadcastReceiver`](https://api.flutter.dev/javadoc/io/flutter/embedding/engine/plugins/broadcastreceiver/package-summary.html), [`ContentProvider`](https://api.flutter.dev/javadoc/io/flutter/embedding/engine/plugins/contentprovider/package-summary.html), or [`Service`](https://api.flutter.dev/javadoc/io/flutter/embedding/engine/plugins/service/package-summary.html). These interfaces declare callbacks that can be invoked by the Flutter engine to notify the plugin when a component is attached or detached. For example, a plugin that requires access to an `Activity` would implement the `ActivityAware` interface and would be notified when the plugin gains or loses access to an `Activity` due to the application being minimized. + +### Creating Geofences + +In order to handle requests, create an instance of `MethodChannel` on the same channel from earlier, and then register the `GeofencingPlugin` instance with this new channel in the implementation of `onAttachedToEngine`: + +```kotlin +override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + mContext = binding.getApplicationContext() + mGeofencingClient = LocationServices.getGeofencingClient(mContext!!) + val channel = MethodChannel(binding.getBinaryMessenger(), "plugins.flutter.io/geofencing_plugin") + channel.setMethodCallHandler(this) +} +``` + +In order to manage these requests, `onMethodCall` needs to be implemented: + +```kotlin +override fun onMethodCall(call: MethodCall, result: Result) { + val args = call.arguments<ArrayList<*>>() + when(call.method) { + "GeofencingPlugin.initializeService" -> { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + mActivity?.requestPermissions(REQUIRED_PERMISSIONS, 12312) + } + // Simply stores the callback handle for the callback dispatcher + initializeService(mContext!!, args) + result.success(true) + } + "GeofencingPlugin.registerGeofence" -> registerGeofence(mContext!!, + mGeofencingClient!!, + args, + result, + true) + "GeofencingPlugin.removeGeofence" -> removeGeofence(mContext!!, + mGeofencingClient!!, + args, + result) + else -> result.notImplemented() + } +} +``` + +Finally, I’ll add the ability to register geofences (removing geofences is relatively trivial, so I’ll focus on adding geofences in this article): + +```kotlin +@JvmStatic +private fun getGeofencingRequest(geofence: Geofence, initialTrigger: Int): GeofencingRequest { + return GeofencingRequest.Builder().apply { + setInitialTrigger(initialTrigger) + addGeofence(geofence) + }.build() +} + +@JvmStatic +private fun getGeofencePendingIndent(context: Context, callbackHandle: Long): PendingIntent { +val intent = Intent(context, GeofencingBroadcastReceiver::class.java) + .putExtra(CALLBACK_HANDLE_KEY, callbackHandle) +return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) +} + +// TODO(bkonyi): Reregister geofences after reboot +// https://developer.android.com/training/location/geofencing +@JvmStatic +private fun registerGeofence(context: Context, + geofencingClient: GeofencingClient, + args: ArrayList<*>?, + result: Result?) { + val callbackHandle = args!![0] as Long + val id = args[1] as String + val lat = args[2] as Double + val long = args[3] as Double + val radius = (args[4] as Number).toFloat() + val fenceTriggers = args[5] as Int + val initialTriggers = args[6] as Int + val expirationDuration = (args[7] as Int).toLong() + val loiteringDelay = args[8] as Int + val notificationResponsiveness = args[9] as Int + val geofence = Geofence.Builder() + .setRequestId(id) + .setCircularRegion(lat, long, radius) + .setTransitionTypes(fenceTriggers) + .setLoiteringDelay(loiteringDelay) + .setNotificationResponsiveness(notificationResponsiveness) + .setExpirationDuration(expirationDuration) + .build() + // Ensure permissions are set properly. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + (context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) + == PackageManager.PERMISSION_DENIED)) { + val msg = "'registerGeofence' requires the ACCESS_FINE_LOCATION permission." + Log.w(TAG, msg) + result?.error(msg, null, null) + } + geofencingClient.addGeofences(getGeofencingRequest(geofence, initialTriggers), + getGeofencePendingIndent(context, callbackHandle))?.run { + addOnSuccessListener { + Log.i(TAG, "Successfully added geofence") + result?.success(true) + } + addOnFailureListener { + Log.e(TAG, "Failed to add geofence: $it") + result?.error(it.toString(), null, null) + } + } +} + +``` + +There’s a lot going on here, so let’s break this down: + +1. Pull the relevant arguments out of the `ArrayList` sent over the `MethodChannel` + +1. Create an instance of `Geofence` that describes the location and size of the geofence as well as its various trigger parameters + +1. Before registering the `Geofence` instance, do another check to ensure that the application still has the correct device permissions for geofencing + +1. Finally, a `GeofencingRequest` as well as a `PendingIntent` are created and used to register the geofence. The `PendingIntent` is used to invoke the `GeofencingBroadcastReceiver` when the geofence is triggered; it contains the callback handle associated with that geofence. + +That’s it! At this point the plugin can create and register a geofence. However, the plugin is not yet ready to handle actual geofence events. For that, the plugin needs to be able to be woken up by the system when there is a geofence event to be handled. + +### Scheduling the geofencing service + +Now that the plugin can register geofence events, it also needs to be able to handle the events themselves. When a geofence registered by the plugin is triggered, Android starts the Flutter application in the background, creates an instance of `GeofencingBroadcastReceiver`, and invokes the overridden `onReceive` method: + +```kotlin +class GeofencingBroadcastReceiver : BroadcastReceiver() { + companion object { + private const val TAG = "GeofencingBroadcastReceiver" + } + + override fun onReceive(context: Context, intent: Intent) { + FlutterMain.startInitialization(context) + FlutterMain.ensureInitializationComplete(context, null) + GeofencingService.enqueueWork(context, intent) + } +} +``` + +The `onReceive` implementation is simple: it ensures that the Flutter framework is initialized and then adds the `Intent` for the geofencing event to the `GeofencingService`’s work queue. Since `GeofencingService` is an implementation of a `JobIntentService`, `GeofencingService.enqueueWork` is simply a wrapper around the `enqueueWork` method in `JobIntentService`, which handles scheduling the work for the service. + +### Handling Geofence Events + +At some point after a geofence event is added to the work queue, Android wakes up the application to invoke `GeofencingService`’s `onHandleWork` method. However, before `onHandleWork` can be called, an instance of `GeofencingService` must be created and initialized by invoking `startGeofencingService` from `GeofencingService`’s `onCreate` method. + +```kotlin +private fun startGeofencingService(context: Context) { + // Synchronize on sServiceStarted to avoid multiple concurrent + // initializations. + synchronized(sServiceStarted) { + mContext = context + // If we don't have an existing background FlutterNativeView, + // we need to create one and have it initialize our callback + // dispatcher. + if (sBackgroundFlutterEngine == null) { + // Grab the callback handle for the callback dispatcher from + // storage. + val callbackHandle = context.getSharedPreferences( + GeofencingPlugin.SHARED_PREFERENCES_KEY, + Context.MODE_PRIVATE) + .getLong(GeofencingPlugin.CALLBACK_DISPATCHER_HANDLE_KEY, 0) + if (callbackHandle == 0L) { + Log.e(TAG, "Fatal: no callback registered") + return + } + // Retrieve the actual callback information needed to invoke it. + val callbackInfo = FlutterCallbackInformation.lookupCallbackInformation(callbackHandle) + if (callbackInfo == null) { + Log.e(TAG, "Fatal: failed to find callback") + return + } + // Create a FlutterEngine which will process the geofencing events. + sBackgroundFlutterEngine = FlutterEngine(context) + + val args = DartCallback( + context.getAssets(), + FlutterMain.findAppBundlePath(context)!!, + callbackInfo + ) + // Start running callback dispatcher code in our background FlutterEngine instance. + sBackgroundFlutterEngine!!.getDartExecutor().executeDartCallback(args) + } + } + // Create the MethodChannel used to communicate between the callback + // dispatcher and this GeofencingService instance. + mBackgroundChannel = MethodChannel(sBackgroundFlutterEngine!!.getDartExecutor().getBinaryMessenger(), + "plugins.flutter.io/geofencing_plugin_background") + mBackgroundChannel.setMethodCallHandler(this) +} +``` + +`startGeofencingService` is responsible for ensuring that the plugin has an associated [`FlutterEngine`](https://api.flutter.dev/javadoc/io/flutter/embedding/engine/FlutterEngine.html) instance. Each `FlutterEngine` instance provides access to a [`DartExecutor`](https://api.flutter.dev/javadoc/io/flutter/embedding/engine/dart/DartExecutor.html) which can be used to execute Dart code in a new isolate. In this case, the `FlutterEngine` instance has the important task of initializing the callback dispatcher, and executing the callbacks registered with the plugin. + +After `startGeofencingService` is done executing, `onHandleWork` is called by the system with the `Intent` that was queued up earlier: + +```kotlin +override fun onHandleWork(intent: Intent) { + // Retrieve the callback handle associated with the triggered geofence. + val callbackHandle = intent. + getLongExtra(GeofencingPlugin.CALLBACK_HANDLE_KEY, 0) + + // Parse the GeofencingEvent from the Intent. + val geofencingEvent = GeofencingEvent.fromIntent(intent) + + if (geofencingEvent.hasError()) { + Log.e(TAG, "Geofencing error: ${geofencingEvent.errorCode}") + return + } + + // Get the geofence transition type (e.g., enter, dwell, exit). + val geofenceTransition = geofencingEvent.geofenceTransition + + // Get the geofences that were triggered. A single event can trigger + // multiple geofences. + val triggeringGeofences = geofencingEvent.triggeringGeofences.map { + it.requestId + } + + val location = geofencingEvent.triggeringLocation + val locationList = listOf(location.latitude, + location.longitude) + val geofenceUpdateList = listOf(callbackHandle, + triggeringGeofences, + locationList, + geofenceTransition) + + synchronized(sServiceStarted) { + if (!sServiceStarted.get()) { + // Queue up geofencing events while background isolate is starting + queue.add(geofenceUpdateList) + } else { + // Callback method name is intentionally left blank. + mBackgroundChannel.invokeMethod("", geofenceUpdateList) + } + } +} +``` + +Most of the above code builds the argument list which is sent to the callback dispatcher. However, before passing the processed geofence event arguments to the callback dispatcher, the plugin must ensure that the callback dispatcher has started listening on its `MethodChannel`. To achieve this behavior, the `GeofencingService` listens for a message from the callback dispatcher, which is sent after the `MethodCall` handler for the dispatcher is set: + +```kotlin +override fun onMethodCall(call: MethodCall, result: Result) { + if (call.method == "GeofencingService.initialized") { + synchronized(sServiceStarted) { + // Dispatch any geofencing events that were handled before the + // callback dispatcher was ready. + while (!queue.isEmpty()) { + mBackgroundChannel.invokeMethod("", queue.remove()) + } + sServiceStarted.set(true) + result.success(null) + } + } else { + result.notImplemented() + } +} +``` + +At this point, the `GeofencingService` is completely initialized and any geofencing events that have queued up are sent to the callback dispatcher. + +## Background execution: iOS (Objective-C) + +Now that the geofencing plugin implementation for Android is finished, the same geofencing functionality needs to be implemented for iOS. + +### Initializing the plugin + +One of the first tasks performed by the Flutter engine at startup is registering and initializing all plugins used by the application. On iOS, this involves invoking the static `registerWithRegistrar` method defined for each plugin. For the geofencing plugin, initialization involves creating an instance of `GeofencingPlugin` and registering it as an application delegate as seen in the code snippet below. This allows for the Flutter engine to delegate handling of certain events to the plugin. + +```objective-c ++ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar { + @synchronized(self) { + if (instance == nil) { + NSLog(@"Registering with registrar"); + instance = [[GeofencingPlugin alloc] init:registrar]; + [registrar addApplicationDelegate:instance]; + } + } +} +``` + +Additional state for the plugin is set when the `GeofencingPlugin` instance is created during plugin registration: + +```objective-c +- (instancetype)init:(NSObject<FlutterPluginRegistrar> *)registrar { + self = [super init]; + NSAssert(self, @"super init cannot be nil"); + + // 1. Retrieve NSUserDefaults which will be used to store callback handles + // between launches. + _persistentState = [NSUserDefaults standardUserDefaults]; + + // 2. Initialize the location manager, and register as its delegate. + _locationManager = [[CLLocationManager alloc] init]; + [_locationManager setDelegate:self]; + [_locationManager requestAlwaysAuthorization]; + _locationManager.allowsBackgroundLocationUpdates = YES; + + // 3. Initialize the Dart runner which will be used to run the callback + // dispatcher. + _headlessRunner = [[FlutterEngine alloc] + initWithName:@"GeofencingIsolate" + project:nil + allowHeadlessExecution:YES]; + _registrar = registrar; + + // 4. Create the method channel used by the Dart interface to invoke + // methods and register to listen for method calls. + _mainChannel = [FlutterMethodChannel + methodChannelWithName:@"plugins.flutter.io/geofencing_plugin" + binaryMessenger:[registrar messenger]]; + [registrar addMethodCallDelegate:self channel:_mainChannel]; + + // 5. Create a second method channel to be used to communicate with the + // callback dispatcher. This channel will be registered to listen for + // method calls once the callback dispatcher is started. + _callbackChannel = + [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/geofencing_plugin_background" + binaryMessenger:_headlessRunner]; + + return self; +} +``` + +### Starting the callback dispatcher + +Once initialization of internal state is complete the callback dispatcher needs to be started by invoking `startGeofencingService`. This is either done when the user calls `GeofencingManager.initialize()` in their application or when the application is started to handle a geofence event (more on this later). + +```objective-c +- (void)startGeofencingService:(int64_t)handle { + NSLog(@"Initializing GeofencingService"); + [self setCallbackDispatcherHandle:handle]; + + FlutterCallbackInformation *info = [FlutterCallbackCache lookupCallbackInformation:handle]; + NSAssert(info != nil, @"failed to find callback"); + + NSString *entrypoint = info.callbackName; + NSString *uri = info.callbackLibraryPath; + [_headlessRunner runWithEntrypointAndLibraryUri:entrypoint libraryUri:uri]; + NSAssert(registerPlugins != nil, @"failed to set registerPlugins"); + + // Once our headless runner has been started, we need to register the application's plugins + // with the runner in order for them to work on the background isolate. `registerPlugins` is + // a callback set from AppDelegate.m in the main application. This callback should register + // all relevant plugins (excluding those which require UI). + registerPlugins(_headlessRunner); + [_registrar addMethodCallDelegate:self channel:_callbackChannel]; +} +``` + +**Note**: the `FlutterMethodChannel` for the callback dispatcher is only registered *after* the headless runner has been started. If an attempt to register the callback dispatcher’s method channel is made before this is done, the application will likely crash. + +### Handling method calls + +Similar to how `onMethodCall` needed to be implemented on Android to allow for the plugin to handle requests from the Dart interface and callback dispatcher, `handleMethodCall` must be implemented: + +```objective-c +- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { + NSArray *arguments = call.arguments; + if ([@"GeofencingPlugin.initializeService" isEqualToString:call.method]) { + NSAssert(arguments.count == 1, + @"Invalid argument count for 'GeofencingPlugin.initializeService'"); + [self startGeofencingService:[arguments[0] longValue]]; + result(@(YES)); + } else if ([@"GeofencingService.initialized" isEqualToString:call.method]) { + // Ignored on iOS. + result(nil); + } else if ([@"GeofencingPlugin.registerGeofence" isEqualToString:call.method]) { + [self registerGeofence:arguments]; + result(@(YES)); + } else if ([@"GeofencingPlugin.removeGeofence" isEqualToString:call.method]) { + result(@([self removeGeofence:arguments])); + } else { + result(FlutterMethodNotImplemented); + } +} +``` + +### Registering geofences + +With initialization completed, the plugin is ready to register for geofence events. When the plugin user requests for a geofence to be set, `registerGeofence` is called: + +```objective-c +- (void)registerGeofence:(NSArray *)arguments { + NSLog(@"RegisterGeofence: %@", arguments); + int64_t callbackHandle = [arguments[0] longLongValue]; + NSString *identifier = arguments[1]; + double latitude = [arguments[2] doubleValue]; + double longitude = [arguments[3] doubleValue]; + double radius = [arguments[4] doubleValue]; + CLCircularRegion *region = + [[CLCircularRegion alloc] initWithCenter:CLLocationCoordinate2DMake(latitude, longitude) + radius:radius + identifier:identifier]; + region.notifyOnEntry = YES; + region.notifyOnExit = YES; + [self setCallbackHandleForRegionId:callbackHandle regionId:identifier]; + [self->_locationManager startMonitoringForRegion:region]; +} +``` + +This method creates the geofence region and uses the `CLLocationManager`’s `startMonitoringForRegion` method to register the geofence. In order to keep track of which callback is associated with the newly registered geofence, the callback handle is mapped to the region’s user provided identifier which is stored to disk using `NSUserDefaults`. Doing this allows for the plugin to lookup the callback handle when a geofence event is received, even if the application had been closed since the geofence was registered. + +### Handling geofence events + +Once the system determines that a geofence has been entered or exited, the `CLLocationManager` invokes one of `didEnterRegion` or `didExitRegion`. At this point, the callback handle for the geofence which was triggered is retrieved from storage and the callback dispatcher is invoked: + +```objective-c +- (void)sendLocationEvent:(CLRegion *)region eventType:(int)event { + NSAssert([region isKindOfClass:[CLCircularRegion class]], @"region must be CLCircularRegion"); + CLLocationCoordinate2D center = region.center; + int64_t handle = [self getCallbackHandleForRegionId:region.identifier]; + [_callbackChannel + invokeMethod:@"" + arguments:@[ + @(handle), + @[ region.identifier ], + @[ @(center.latitude), @(center.longitude) ], + @(event) + ] + ]; +} + +- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region { + [self sendLocationEvent:region eventType:kEnterEvent]; +} + +- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region { + [self sendLocationEvent:region eventType:kExitEvent]; +} +``` + +### Geofence events in a suspended state + +If you are familiar with developing applications for both Android and iOS, you’ll probably know that iOS is much more restrictive than Android when it comes to executing code in the background. Instead of spawning potentially long-running services to handle background events, iOS allows for applications to register for specific types of events which, when received, wake up the application and invoke any relevant delegates. Since the [`FlutterPlugin`](https://docs.flutter.io/objcdoc/Protocols/FlutterPlugin.html) protocol allows for plugins to be registered as delegates, it’s relatively simple to handle any background event provided by the system. + +For geofencing, the plugin needs to implement `didFinishLaunchingWithOptions` which is invoked when the application has just been started and is ready to run. The dictionary parameter of this method will contain `UIApplicationLaunchOptionsLocationKey` if the application was launched due to a geofence event. + +```objective-c +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + + // Check to see if we're being launched due to a location event. + if (launchOptions[UIApplicationLaunchOptionsLocationKey] != nil) { + // Restart the headless service. + [self startGeofencingService:[self getCallbackDispatcherHandle]]; + } + + // Note: if we return NO, this vetos the launch of the application. + return YES; +} +``` + +If the application is launched as the result of a geofence being triggered, the callback dispatcher for the plugin will still need to be initialized by calling `startGeofencingService` with the cached callback dispatcher handle. After returning from this method, the location manager will invoke the appropriate handler described in the previous section for the geofence event. + +## Usage example: operating a garage door with geofencing + +Now that the geofencing plugin is fully implemented for both Android and iOS, I can finally put it to good use: automatically opening my garage door as I pedal towards my house! + +<DashImage figure src="images/0ae_YppUn96m93qkT.webp" /> + + +<DashImage figure src="images/0at7FCzjM5lAd8NC_.webp" alt="The relay is triggered by the Raspberry Pi, which opens and closes the door (left). A proximity sensor allows for the garage door service to know whether or not the door is currently open (right)." caption="The relay is triggered by the Raspberry Pi, which opens and closes the door (left). A proximity sensor allows for the garage door service to know whether or not the door is currently open (right)." /> + + +<DashImage figure src="images/074sQ7Fl7ueommzJy.webp" alt="*My garage door remote, built using Flutter. Can you tell that I’m a backend engineer?*" caption="*My garage door remote, built using Flutter. Can you tell that I’m a backend engineer?*" /> + + +<iframe src="https://gfycat.com/WickedHospitableEidolonhelvum" width="1920" height="1080" frameborder="0" allowfullscreen></iframe> + + +Over the past couple of months I’ve been tinkering with a Raspberry Pi and the Dart [rpi_gpio package](https://pub.dartlang.org/packages/rpi_gpio/versions/0.4.0-dev.1#-readme-tab-) to get back into working with circuits. I had been toying around with the idea of making a Flutter application that would open my garage door for quite awhile, so it made sense for my first hardware project to use Dart and Flutter. Over a couple of weekends, I wrote a [service to control the garage door](https://github.com/bkonyi/GarageDoorController/blob/master/bin/garage_server.dart), performed minor surgery to wire a relay across the opener button, installed a proximity sensor to query the state of the door, and finally wrote a [simple application using Flutter that functions as a remote control](https://github.com/bkonyi/FlutterGarageDoorOpener). + +After using my solution for a couple of weeks, I was becoming annoyed. Although it was great that I could open the garage with a mobile application, I still had to pull out my phone, open the app, and then manually trigger the door. This is particularly painful for me since fingerprint readers apparently don’t like to work when covered in sweat, making it difficult to unlock my phone (I don’t have a car and I commute 15km each way by bike, so fingerprint reader struggles are a daily occurrence). + +Luckily for me, Flutter now has a geofencing plugin that can perform tasks even while the application isn’t open, including opening my garage door! + +### Setting the right permissions + +The first thing I’ll do is ensure that my application has the right permissions to use the geofencing plugin. + +### Permissions: Android + +In order for the geofencing plugin to run in the background, `GeofencingBroadcastReceiver` and `GeofencingService` need to be registered in AndroidManifest.xml: + +```xml +<receiver android:name="io.flutter.plugins.geofencing.GeofencingBroadcastReceiver" +android:enabled="true" android:exported="true"/> + +<service android:name="io.flutter.plugins.geofencing.GeofencingService" +android:permission="android.permission.BIND_JOB_SERVICE" android:exported="true"/> +``` + +Geofencing on Android also requires the `ACCESS_FINE_LOCATION` and `ACCESS_BACKGROUND_LOCATION` (Android 10+) permissions to be requested in AndroidManifest.xml: + +```xml +<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> +<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/> +``` + +### Permissions: iOS + +Requesting the relevant permissions on iOS requires some simple modifications to `Info.plist` in the ios directory of the application. First, add the following lines to request background location updates: + +```openstep property list +<dict> +… + <key>UIRequiredDeviceCapabilities</key> + <array> + <string>location-services</string> + <string>gps</string> + <string>armv7</string> + </array> + <key>UIBackgroundModes</key> + <array> + <string>location</string> + </array> +… +</dict> +``` + +Then set the `NSLocation` description messages: + +```openstep property list +<dict> +… + <key>NSLocationAlwaysAndWhenInUseUsageDescription</key> + <string>YOUR DESCRIPTION HERE</string> + <key>NSLocationWhenInUseUsageDescription</key> + <string>YOUR DESCRIPTION HERE</string> +… +</dict> +``` + +These descriptions are shown to the user when the application requests access to their location. **If they’re not provided, geofencing registration will fail silently!** + +```objective-c +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +// Add the import for the GeofencingPlugin. +#import <geofencing/GeofencingPlugin.h> + +void registerPlugins(NSObject<FlutterPluginRegistry>* registry) { + [GeneratedPluginRegistrant registerWithRegistry:registry]; +} + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Register the plugins with the AppDelegate + registerPlugins(self); + // Set registerPlugins as a callback within GeofencingPlugin. This allows + // for the Geofencing plugin to register the plugins with the background + // FlutterEngine instance created to handle events. If this step is skipped, + // other plugins will not work in the geofencing callbacks! + [GeofencingPlugin setPluginRegistrantCallback:registerPlugins]; + + // Override point for customization after application launch. + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end +``` + +Finally, similarly to what was done for Android, set a reference to the application’s plugin registrant within `GeofencingPlugin` from the application’s `AppDelegate`. This is needed to register the application’s plugins with the geofencing plugin’s background isolate, which makes it possible to use other plugins in the context of that isolate. + +### Bringing it all together + +Now that the permissions required for geofencing have been set correctly, all that’s left to do is wiring the plugin into the garage door remote application. + +First of all, the plugin needs to be initialized. This is done in a method named `initialize`, which is invoked when the application starts: + +```dart +Future<void> initialize() async { + // Perform other initialization + // … + // Initialize the geofencing plugin. + await GeofencingManager.initialize(); +} +``` + +Next, there should be some way to toggle whether or not the garage door should open when the geofence is entered. This can be accomplished with a simple `Switch` displayed at the bottom of the application: + +```dart +Widget _proximityTriggerToggle() => Container( + padding: const EdgeInsets.fromLTRB(26.0, 2.5, 26.0, 2.5), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: <Widget>[ + const Text('Proximity Trigger', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0)), + Switch( + value: _proximityTriggerEnabled, + onChanged: (bool state) async { + setState(() { + _proximityTriggerEnabled = state; + }); + if (state) { + await GeofencingManager.registerGeofence( + GeofenceTrigger.homeRegion, + GeofenceTrigger.homeGeofenceCallback); + } else { + await GeofencingManager.removeGeofence( + GeofenceTrigger.homeRegion); + } + }, + ), + ], + ) + ); + +``` + +Depending on the state of this `Switch`, the application either registers a geofence with the callback used to open the garage door, or removes the geofence. + +Finally, the geofence region around my home and its corresponding callback are defined in `GeofenceTrigger`: + +```dart +abstract class GeofenceTrigger { + static final _androidSettings = AndroidGeofencingSettings( + initialTrigger: [GeofenceEvent.exit], + notificationResponsiveness: 0, + loiteringDelay: 0); + + static bool _isInitialized = false; + + static final homeRegion = GeofenceRegion( + 'home', HOME_LAT, HOME_LONG, 300.0, <GeofenceEvent>[GeofenceEvent.enter], + androidSettings: _androidSettings); + + static Future<void> homeGeofenceCallback( + List<String> id, Location location, GeofenceEvent event) async { + // Check to see if this is the first time the callback is being called. + if (!_isInitialized) { + // Re-initialize state required to communicate with the garage door + // server. + await initialize(); + _isInitialized = true; + } + + if (event == GeofenceEvent.enter) { + await GarageDoorRemote.openDoor(); + } + } +} +``` + +For my initial tests I created a `GeofenceRegion` with a radius of 300m around my home that will trigger at some point after I enter the area. Once the geofence is entered the `homeGeofenceCallback` is invoked, checks are performed to ensure the application can communicate with the garage door server, and then a request to open the door is sent. Once I confirmed that the logic within the callback actually triggered the garage door to open using a third-party application to mock my location and movements, it was time to do some real world testing on my bike! + +After a few trips up and down the street it became apparent that, although geofences are triggered almost immediately when using a mocked location, Android provides no guarantees as to when a geofence event is delivered. Unfortunately, this means that it can potentially take minutes before my garage door remote is notified that I’ve entered the geofence region around my home. With a radius as small as 300m, I often found myself waiting a minute or two for the door to open on its own. + +The temporary fix for this was to increase the radius of the geofence region to 1km, which seems to work well enough for now. Obviously, there’s some issues with this approach, but I plan on further refining the proximity triggering logic to use a larger geofence that will start more frequent location updates. These location updates will then be used to manually determine whether I’m within a certain radius of my house, at which point the request to open the door will be sent. + +## Conclusion + +If you’ve made it this far you should now have all the knowledge you need to create a Flutter plugin that executes Dart code in the background! Of course, I was only able to scratch the surface of plugin development for Flutter in this article but I would highly recommend the documentation for [developing packages and plugins](https://flutter.io/developing-packages/) as well as the walkthrough for [writing custom platform-specific code with platform channels](https://flutter.io/platform-channels/). If you want a deeper look into how the geofencing plugin is implemented, the full source can be found in the [FlutterGeofencing](https://github.com/bkonyi/FlutterGeofencing) repo on GitHub. I’ve also linked to all the additional documentation, sample plugins, and projects mentioned throughout this article in the Resources section below. + +I’ve had a lot of fun implementing background execution support for Flutter, and even more fun creating the geofencing plugin for this article. (It was a wonderful excuse to work on a personal project as part of my job!) If you feel so inclined, [follow me on GitHub](https://github.com/bkonyi) to keep up with my work on Flutter and the Dart virtual machine, as well as my other pet projects. + +Thanks for reading and happy Fluttering! + +<DashImage figure src="images/0D6jZA_ksvRN5EJi1.webp" alt="Like many members of the Dart and Flutter teams, Dash loves cycling. However, for obvious reasons, Dash has a bit of trouble riding a bike." caption="Like many members of the Dart and Flutter teams, Dash loves cycling. However, for obvious reasons, Dash has a bit of trouble riding a bike." /> + + +## Resources + +### Docs for Dart: + +* [**PluginUtilities**](https://api.flutter.dev/flutter/dart-ui/PluginUtilities-class.html): contains methods for dealing with CallbackHandles + +* [**IsolateNameServer**](https://api.flutter.dev/flutter/dart-ui/IsolateNameServer-class.html): establishing communication between foreground and background isolates using named SendPorts + +### Docs for Android: + +* [**Flutter Java APIs**](https://api.flutter.dev/javadoc/overview-summary.html) + +* [**FlutterEngine**: allows for spawning a background isolate](https://api.flutter.dev/javadoc/io/flutter/embedding/engine/FlutterEngine.html) + +* [**FlutterCallbackInformation**](https://api.flutter.dev/javadoc/io/flutter/embedding/engine/FlutterEngine.html): lookup callback information needed for starting a background isolate using callback handles + +### Docs for iOS: + +* [**FlutterEngine**](https://api.flutter.dev/objcdoc/Classes/FlutterEngine.html): allows for spawning a background isolate + +* [**FlutterCallbackCache**](https://api.flutter.dev/objcdoc/Classes/FlutterCallbackCache.html): lookup callback information needed for starting a background isolate using callback handles + +### Sample Plugins: + +* [**FlutterGeofencing**](https://github.com/bkonyi/FlutterGeofencing): the geofencing plugin created for this article + +* [**android_alarm_manager**](https://pub.dartlang.org/packages/android_alarm_manager#-readme-tab-): an AlarmManager plugin for Flutter + +* [**background_location**](https://pub.dev/packages/background_location): plugin for listening for significant location changes on Android and iOS + +### Projects referenced: + +* [**rpi_gpio**](https://github.com/danrubel/rpi_gpio.dart): package for accessing the Raspberry Pi GPIO pins. + +* [**FlutterGarageDoorOpener**](https://github.com/bkonyi/FlutterGarageDoorOpener): garage door remote control using Flutter + +* [**GarageDoorController**](https://github.com/bkonyi/GarageDoorController): the garage door controller library and service \ No newline at end of file diff --git a/sites/www/content/blog/extreme-ui-adaptability-in-flutter-how-google-earth-supports-every-use-case-on-earth/images/17hP0qPviv5Hrh7U82WWhQg.gif b/sites/www/content/blog/extreme-ui-adaptability-in-flutter-how-google-earth-supports-every-use-case-on-earth/images/17hP0qPviv5Hrh7U82WWhQg.gif new file mode 100644 index 0000000000..b6d9a5121b Binary files /dev/null and b/sites/www/content/blog/extreme-ui-adaptability-in-flutter-how-google-earth-supports-every-use-case-on-earth/images/17hP0qPviv5Hrh7U82WWhQg.gif differ diff --git a/sites/www/content/blog/extreme-ui-adaptability-in-flutter-how-google-earth-supports-every-use-case-on-earth/images/1RXf-GaJ4uz5_ZKU8QgIVkw.webp b/sites/www/content/blog/extreme-ui-adaptability-in-flutter-how-google-earth-supports-every-use-case-on-earth/images/1RXf-GaJ4uz5_ZKU8QgIVkw.webp new file mode 100644 index 0000000000..9c6daa21d9 Binary files /dev/null and b/sites/www/content/blog/extreme-ui-adaptability-in-flutter-how-google-earth-supports-every-use-case-on-earth/images/1RXf-GaJ4uz5_ZKU8QgIVkw.webp differ diff --git a/sites/www/content/blog/extreme-ui-adaptability-in-flutter-how-google-earth-supports-every-use-case-on-earth/images/1nHhbCY1mlIwlvQI60jaJHA.webp b/sites/www/content/blog/extreme-ui-adaptability-in-flutter-how-google-earth-supports-every-use-case-on-earth/images/1nHhbCY1mlIwlvQI60jaJHA.webp new file mode 100644 index 0000000000..1d6dd4bd49 Binary files /dev/null and b/sites/www/content/blog/extreme-ui-adaptability-in-flutter-how-google-earth-supports-every-use-case-on-earth/images/1nHhbCY1mlIwlvQI60jaJHA.webp differ diff --git a/sites/www/content/blog/extreme-ui-adaptability-in-flutter-how-google-earth-supports-every-use-case-on-earth/index.md b/sites/www/content/blog/extreme-ui-adaptability-in-flutter-how-google-earth-supports-every-use-case-on-earth/index.md new file mode 100644 index 0000000000..9d881cf46e --- /dev/null +++ b/sites/www/content/blog/extreme-ui-adaptability-in-flutter-how-google-earth-supports-every-use-case-on-earth/index.md @@ -0,0 +1,259 @@ +--- +title: "Extreme UI Adaptability in Flutter — How Google Earth supports every use case on earth" +description: "When Google Earth set out to rewrite their mobile and web clients in Flutter, they knew they wanted to allow each and every one of their…" +publishDate: 2024-02-07 +author: craiglabenz +image: images/1RXf-GaJ4uz5_ZKU8QgIVkw.webp +category: spotlight +layout: blog +--- + +## Extreme UI Adaptability in Flutter — How Google Earth supports every use case on earth + +When Google Earth set out to rewrite their mobile and web clients in Flutter, they knew they wanted to allow each and every one of their users to explore the planet however they liked, on whatever devices they owned. This had long been true to an extent; after all, Google Earth had existing web, desktop, Android, and iOS clients. But this rewrite, which would cover all of those targets other than desktop, would need to support the superset of existing use cases; plus a few new adaptability ideas the Earth team was excited to explore. + +The search for Google Earth’s tech stack of the future was heavily informed by all-too-familiar sources of friction that had slowed development on their existing clients. Namely, Google Earth had long been forced to choose between developer velocity on new features and maintaining feature parity across their three unique codebases (web, Android, and iOS). Luckily, the center of the UI — the entirety of the pale blue dot in the middle of the screen — is powered by a C++ engine that already delivered a unified experience for *some* of Google Earth’s features. However, the rest of the UI chrome and menus were implemented separately across each codebase. This meant that any cross-platform choice would not only need to overhaul the UI development process, but also integrate with a large legacy engine in Android, iOS, and the web. + +The deciding factors to use Flutter were twofold. First, integrating with the existing Google Earth engine proved to be a straightforward task using method channels. Second, Google Earth not only wanted to streamline their codebases, but also to reimagine their UI entirely. Any major UI overhaul is already something of a rewrite, and Google Earth opted to write one new Flutter app instead of performing surgery on three existing apps. This complicated the task, but the team committed to a clean break and obsessive focus on adaptability. In the end, the Google Earth team came to power their UI across three platforms with Flutter. + +## **Defining adaptability** + +And so, the Google Earth team set out on an adventure to push the limits of UI adaptability. Prior art abounds on creating UIs tailored to different user journeys — dating back to the dawn of smartphones and the entire internet’s collective realization that most websites needed a rethink for small screens. Browser APIs and CSS patterns emerged to build websites with an awareness of their screen’s resolution; and those ideas have been prominent ever since. Even in Flutter’s earliest days, developers knew phone screens would vary and made their apps’ UIs depend on the screen’s resolution. And if that resolution changed — either because the user rotated their phone or resized their browser window, the app’s UI would *respond*. In Flutter, as in the web for years before it, ***responsive UIs*** improved user experiences. + +What then, you might wonder, is the difference between a *responsive* UI and an *adaptive* UI? Put simply, a *responsive* UI adjusts to changes in the amount and aspect ratio of available pixels; while an *adaptive* UI adjusts to *everything else.* Responsive UIs can grow and shrink individual UI elements based on screen real estate details, but adaptive UIs answer more fundamental questions like where to render the app’s navigation, whether list views should route to separate detail views or show them side-by-side with the list itself, and how the user’s connected peripherals should influence things like tap targets and hover states (more on this concept later). +> For more on this, watch [episode 15 of Decoding Flutter](https://youtu.be/HD5gYnspYzk?si=8AvuBRGXNRNET9dR) on Adaptive vs Responsive UIs and see these guides from [Flutter](https://docs.flutter.dev/ui/layout/responsive/building-adaptive-apps) and [Android’s](https://developer.android.com/develop/ui/views/layout/responsive-adaptive-design-with-views) documentation. + +As anyone who’s written responsive CSS for a website will tell you, even simple UIs promise tricky edge cases. And to be clear, this is no fault of CSS; the problem space’s many states are so fine-grained as to almost feel analog. What then, should a UI developer expect when considering several additional variables, such as the device form factor and connected peripherals? Naturally, they should expect a fair increase in complexity. + +This all came to a head when the behavior of an early prototype caught the Google Earth team off-guard. While playing with that early build, a Google Earth engineer shrunk their desktop web browser down to an extremely narrow width. Suddenly, typical desktop affordances like side navigation bars and tighter touch targets were replaced by mobile affordances, like a bottom navigation bar and larger, finger-friendly buttons. Their surprise was brief — after all, *that was exactly what they’d told their app to do*. The Google Earth team was now faced with a profound question — *Is this what a user would want?* + +Such was the terra incognita the Google Earth team was about to chart. + +## Why adaptability? + +To some, the following content raises a meta-question: *Why bother with any of this in the first place? Is the ROI sufficient when surely a responsive UI will satisfy most users?* + +These are good questions, but they should not contribute toward hesitation with Flutter. Using a cross-platform UI framework like Flutter does not *introduce* adaptive UI concerns; it *unlocks adaptive UI solutions*. Beyond that, here are two considerations that suggest adaptive UIs really are that important: + +* Screen resolutions don’t imply what they once did. Desktop browsers can have low DPI settings that a naive breakpoint check will confuse with mobile environments; high-DPI phones in landscape orientation can be mistaken with old tablet (or even desktop!) breakpoints; and foldable devices can alternate between showing your app full-screen and splitting screen real estate between multiple apps, leading to jarring differences if this careens a user back and forth across certain breakpoints. + +* Apps with distinct creation vs consumption modes (think any text composition app with Read and Edit experiences) can suffer heavily on mobile — and especially on tablets. Shipping a mobile-first, and thus likely consumption-first experience to smartphones and tablets greatly limits your power users with a tablet, Bluetooth keyboard, and mouse. + +## Delivering on adaptability + +The Google Earth team walked a long road of experimentation, user research, and iteration to arrive at the app they ultimately shipped. But in the end, their problem space boiled down to three high-level questions: + +1. How should the app determine its initial UI strategy? + +1. How and when should the app change its UI strategy? + +1. How would the Google Earth team cleanly implement this logic? + +## Determining an initial UI strategy + +One of the Earth team’s early assumptions was that “there is no difference between a Chromebook with a touchscreen and a tablet with a connected Bluetooth keyboard”, and that their UI should not distinguish between the two. Although this idea stood to initial reason, it did not survive testing; and over time the Earth team increasingly realized the gaps in this approach. A user launching the app with a high-resolution tablet in landscape mode could find themselves within a desktop UI range of pixel resolution (following older, responsive UI rules). If that same user then rotated their tablet into portrait mode and in doing so shifted into a pixel resolution range assigned to tablets, Google Earth would be faced with a hard choice. The dynamic option would be to dramatically restructure everything by shifting from the desktop UI to the mobile UI; whereas the static option would be to do nothing except squish and compress the desktop UI until it fit in within its new constraints. Neither of these options were satisfying, and it all meant that there *was* a difference between a Chromebook with a touchscreen and a tablet with a keyboard. + +In the end, the Earth team settled on a simple rule: serve the mobile experience to smartphones and tablets, and the desktop experience to desktops. If this seems anti-climactic, well, it sort of is; but only because it punts some of the juicy parts to the next question — *When should the UI’s initial strategy **change?*** + +## Updating the UI strategy within a user session + +The Earth team’s first strategy for UI changes was little more than established responsive UI rules: show your mobile UI on any resolutions below a minimum threshold, your tablet UI (if you have one) the next few hundred possible widths, and lastly, your desktop UI on anything else. And, critically, when a UI crosses one of those thresholds for any reason, you re-render the app accordingly. Of course, this ruleset’s awkwardness launched Google Earth onto its odyssey of extreme adaptability; so it should be no surprise that the team abandoned this approach. + +A second possibility came from Stadia, a fellow Google team with a successful Flutter mobile app. (Obviously, Stadia did not survive as a product; but that was not for lack of functionality in its mobile app!) Stadia’s approach was to make adaptive UI decisions based on which inputs were last touched. Drag your computer’s cursor or press a key, and Stadia would snap into its desktop UI mode. Conversely, if you tilted a joystick on a connected console controller, Stadia would snap into its console UI mode. However, while that made sense for Stadia, it proved less appropriate for Google Earth. A simple case ruled out this last-inputs-touched strategy: a tablet user pinching to zoom their map, then returning to a Bluetooth keyboard to finish typing content. No user would want two dramatic UI transitions during that simple interaction, so the user’s most recent inputs could not wholesale change Google Earth’s UI from mobile to desktop or back. + +In the end, the Google Earth team settled on a second very simple rule: remain consistent within a session and never leave the initial UI flavor without the user’s explicit permission. As explored earlier, Google Earth would show its mobile-first UI on smartphones and tablets and its desktop-first UI on desktops; and it would never outsmart itself and change that unless the user requested a change in the settings panel. + +## Mixed-UI states + +UI consistency within sessions served Google Earth well, but it is not the whole story. UI affordances in desktop experiences like cursor hover effects lack any equivalent on mobile and must be reimagined. A user treating their touchscreen laptop like a tablet could be blocked entirely by an app’s failure to replace critical hover effects with alternatives suitable for mobile. This realization suggested a two-tier problem and solution. Google Earth’s UI would not only need to smoothly switch back and forth between its mobile and desktop experiences when a user requested, but individual controls would need to have both a touch-friendly form *and* a mouse-friendly form, regardless of the overarching strategy. + +Finally, Google Earth knew what they were building. All of their research and iteration left only implementation questions, which amounted to: + +1. How to manage transitions between two fundamentally different UIs, and + +1. How to build individual controls to support atypical peripherals + +## Managing multiple UIs + +At its simplest, building any Flutter app to seamlessly switch between two different experiences is as simple as putting the following line somewhere in a widget’s build method: + +```dart +child: mode == Mode.desktop ? DesktopUI() : MobileUI() +``` + + +However, this strategy (which is what Google Earth uses) implies some extra work elsewhere to fully realize. The issue — initially obscure — surfaces when *any* application state is stored within a Stateful widget, as toggling that `mode` variable completely replaces the widget tree, destroying all State objects and any information they hold. There are two layers to this problem. + +To imagine the first layer, consider a screen that has multiple panels on desktop, but reorganizes each of those panels into a tab bar experience on mobile. A mobile user will have an active tab, but that concept has no equivalent on desktop. Storing the active tab index within a StatefulWidget (an idiomatic decision in Flutter!) would always reset a mobile user’s position to the default tab after toggling back and forth through the desktop UI. The solution to this involves moving any primitive application state — strings, integers, and the like — out of StatefulWidgets and into your state management classes. This way, no shenanigans in your widget tree can reset critical values. + +The problem’s second layer comes from application state less easily pulled out of the widget tree, like TextEditingControllers or ScrollControllers. The situation looks like this: you have a ListTile with a TextField, but any time the user touches their mouse or touchscreen, you rebuild that ListTile to accommodate the user’s latest peripherals. Without intervention, this would cause Flutter to destroy the entire part of the Widget and Element trees containing the old TextField, taking with them any controllers holding the user’s work. You might be tempted to treat these as primitives (TextEditingControllers as strings and ScrollControllers as doubles) and repeat the above solution; but controllers are too rich to easily serialize in this way (cursor position and text selection, anyone?). + +To solve this problem, Google Earth uses GlobalKeys to have the framework “reparent” highly-scoped widgets after a fresh layout. The following AdaptableTextInput widget is tightly scoped to its TextField and TextEditingController. Supplying the same GlobalKey to that AdaptableTextInput widget across UI-changing rebuilds will keep the TextEditingController alive, saving your users’ work. + +```dart +class AdaptableTextInput extends StatefulWidget { + + // Supply a consistent GlobalKey here! + const AdaptableTextInput({super.key, required this.mode}); + + final Mode mode; + + @override + State<AdaptableTextInput> createState() => _AdaptableTextInputState(); +} + +class _AdaptableTextInputState extends State<AdaptableTextInput> { + + final _controller = TextEditingController(); + final String helpText = 'I clarify this text input!'; + + @override + Widget build(BuildContext context) { + if (widget.mode == Mode.desktop) { + return Tooltip( + showOnHover: helpText, + child: TextField(controller: _controller), + ); + } else if (widget.mode == Mode.mobile) { + return Column( + children: <Widget>[ + TextField(controller: _controller), + Text(helpText); + ], + ); + } + } +} +``` + + +## Navigation + +Navigation stacks and the app’s Back button also require special attention. Continuing with the above example of a desktop UI that shows multiple panels at once, now imagine a complementary mobile UI that presents those panels in a stack-like UI with forward and backward navigation. The implications of allowing desktops to use the mobile UI, and phones to use the desktop UI, was one of the big adaptability ideas Google Earth wanted to pursue. + +<DashImage figure src="images/1nHhbCY1mlIwlvQI60jaJHA.webp" alt="A grid of UIs, showing a desktop UI on both desktop and mobile devices, and a mobile UI on both desktop and mobile devices" caption="A grid of UIs, showing a desktop UI on both desktop and mobile devices, and a mobile UI on both desktop and mobile devices" /> + + +If a desktop-UI user is on the red panel when they switch to the mobile UI, the Back button won’t automatically be wired up, because the navigation stack will be reset. This means your desktop UI needs to account for extra information technically only needed by the mobile UI, because at any moment the mobile UI could be asked to take over. + +<DashImage figure src="images/1RXf-GaJ4uz5_ZKU8QgIVkw.webp" alt="A desktop device rendering the same UI in two different modes — one typical of desktop, and one typical of mobile" caption="A desktop device rendering the same UI in two different modes — one typical of desktop, and one typical of mobile" /> + + +Luckily, GoRouter’s declarative routing API can help. Create two separate routing declarations and switch to the appropriate route when your user toggles UI modes. In this situation, if the desktop UI has tracked the user’s last activity to the red panel when a request to activate the mobile UI comes in, calling `mobileRouter.go(‘home/blue/red’)` will create a navigation stack with a synthetic history, allowing the user to press the Back button to escape the red screen. + +```dart +final desktopRouter = GoRouter( + routes: [ + GoRoute( + path: '/home', + builder: (context, state) => FourPanels(), + ), + ], +); + +final mobileRouter = GoRouter( + routes: [ + // One route for each panel, configured to wire up + // the Back button if a user arrives on one of the nested panels + GoRoute( + path: '/home/blue', + builder: (context, state) => BluePanel(), + routes: [ + GoRoute( + path: 'red', + builder: (context, state) => RedPanel(), + routes: [ + GoRoute( + path: 'green', + builder: (context, state) => GreenPanel(), + routes: [ + GoRoute( + path: 'yellow', + builder: (context, state) => YellowPanel(), + ), + ], + ), + ], + ), + ], + ), + ], +); +``` + + +Highly adaptive UIs like Google Earth’s require an implementation that treats all possible scenarios as always in play, even though only one given UI is ever being rendered. This means that the app must always be able to *completely* reconstruct its state from resources you completely control — whether that is because you have GlobalKeys to retain State objects holding important information, or because you’ve stored all relevant details in your state management classes. + +## Adapting to user inputs + +All of this left only one more tricky adaptability problem: ensuring controls across their UI were amenable to the user’s last-used peripherals and not just the reigning UI strategy. After all, if a tablet user started clicking a Bluetooth mouse; Google Earth wasn’t going to wholesale switch to their desktop UI, but they *did* want to slightly tweak elements to leverage a keyboard and mouse’s strengths. + +Merely using Flutter meant Google Earth was off to a good start here. Imagine the alternative: an app that is split across three codebases (JavaScript for desktop via web, and Swift and Kotlin for mobile), when the Swift and Kotlin teams realize that it would be awfully nice if, *in some scenarios*, they could borrow elements from the JavaScript app’s UI. Maybe what they need can be re-implemented simply enough; or maybe not. Either way; in a Flutter app, the existing tool you want to borrow is always in the same codebase. + +But code sharing is not code organization, and the question of how to implement this coherently remained. Here, the Google Earth team turned to an old Flutter staple: the builder pattern. + +```dart +/// High level categories of user inputs. +enum InputType { gamepad, keyboardAndMouse, touch } + +/// Builds a widget tree that depends on the user's current [InputType]. +class InputTypeBuilder extends StatelessWidget { + /// Called when the [InputType] data updates. + final Function(BuildContext, InputTypeModel, Widget?) builder; + + /// Constructs a wrapping widget that will invoke the [builder] whenever + /// [InputType] changes. + /// + /// See [InputTypeModel] for details on how to change [InputType]. + const InputTypeBuilder({ + Key? key, + required this.builder, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Consumer<InputTypeModel>( + builder: (context, inputTypeModel, _) { + return builder( + context, + inputTypeModel.inputType, + ); + }, + ); + } +} +``` + + +A widget like `InputTypeBuilder` listens to a top-level mechanism, the `InputTypeModel`, which itself subscribes to the Flutter Engine for updates on the last-used input. `InputTypeModel.inputType` is a property of the `InputType` enum. And with that, child widgets can make localized decisions about how to render themselves in light of how the user is currently interacting with the app. For example, if you had been using a mouse, but then tapped your finger on the touch screen, affordances that were once only revealed by the cursor’s hover effect would now appear all over the app. And similarly, if you switched back to using the mouse, this `InputTypeBuilder` would allow them to reverse the change. + +```dart +@override +Widget build(BuildContext context) { + return InputTypeBuilder( + builder: (context, inputTypeModel, child) { + final bool isHoveredOrTouchInput = isHovered || inputTypeModel.inputType == InputType.touch; + return Row( + children: <Widget>[ + isHoveredOrTouchInput ? DragIndicator() : Container(), + RestOfRow(), + ], + ); + }, + ); +} +``` + + +The following gif shows Google Earth’s desktop UI (running in Chrome), nimbly adjusting to the user alternating between touchscreen and mouse actions. + +<DashImage figure src="images/17hP0qPviv5Hrh7U82WWhQg.gif" alt="Google Earth’s UI swapping between typical desktop and mobile affordances as the end-user interacts with different peripherals" caption="Google Earth’s UI swapping between typical desktop and mobile affordances as the end-user interacts with different peripherals" /> + + +## Conclusion + +The biggest unexpected wins rebuilding Google Earth with Flutter came to users of the tweener environments — tablets and the web. Caught awkwardly between phones and laptops; tablets can physically support both types of experiences but rarely enjoy the software flexibility to match. Similarly, web experiences can be loaded on any device; and on desktop, browsers can be arbitrarily resized. Depending on the app, all of this can imply radically different UIs. For most development teams with separate codebases for each build target, fully supporting users caught in these limbo states is a non-starter. (Imagine convincing your boss to spend the time rebuilding your entire desktop UI on mobile, just in case a tablet user wants it!) + +But, as the Google Earth team found, while building a fully adaptive UI in one codebase did imply extra complexity, it was dwarfed by the user experience improvements gained by meeting each and every user exactly where they were. + +You can try Google Earth’s new Flutter implementation today by downloading the app on Android or iOS, or visiting [https://earth.google.com](https://earth.google.com). \ No newline at end of file diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/13l-v0ElBcVHNAEwhei1ZkA.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/13l-v0ElBcVHNAEwhei1ZkA.webp new file mode 100644 index 0000000000..9fb1c89333 Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/13l-v0ElBcVHNAEwhei1ZkA.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/14KjDPJFvTyV_8eRVSwug5w.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/14KjDPJFvTyV_8eRVSwug5w.webp new file mode 100644 index 0000000000..9394cd6449 Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/14KjDPJFvTyV_8eRVSwug5w.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/15pbF1OIADrJvldPPBOjTYg.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/15pbF1OIADrJvldPPBOjTYg.webp new file mode 100644 index 0000000000..400064bd69 Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/15pbF1OIADrJvldPPBOjTYg.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/16-qalTHst8nv6KOohptX1Q.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/16-qalTHst8nv6KOohptX1Q.webp new file mode 100644 index 0000000000..ff7ceafe67 Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/16-qalTHst8nv6KOohptX1Q.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/18MiFLAgG8cCpTtqTeX1v8Q.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/18MiFLAgG8cCpTtqTeX1v8Q.webp new file mode 100644 index 0000000000..b2b097df92 Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/18MiFLAgG8cCpTtqTeX1v8Q.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1B5i6MSUhz554dVTR484AuA.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1B5i6MSUhz554dVTR484AuA.webp new file mode 100644 index 0000000000..c2b4bd2b53 Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1B5i6MSUhz554dVTR484AuA.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1DOGxw3bdrfqppiG2ZobhIQ.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1DOGxw3bdrfqppiG2ZobhIQ.webp new file mode 100644 index 0000000000..aefe1d8509 Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1DOGxw3bdrfqppiG2ZobhIQ.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1JZTY90OF41GsrvE6hFAZ3g.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1JZTY90OF41GsrvE6hFAZ3g.webp new file mode 100644 index 0000000000..08f746d1af Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1JZTY90OF41GsrvE6hFAZ3g.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1LcMq0sqB0w-n-3C-Zr3V-w.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1LcMq0sqB0w-n-3C-Zr3V-w.webp new file mode 100644 index 0000000000..1a9751ded7 Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1LcMq0sqB0w-n-3C-Zr3V-w.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1OEZMrFP7te_MknrCaSuDiw.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1OEZMrFP7te_MknrCaSuDiw.webp new file mode 100644 index 0000000000..68200d465c Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1OEZMrFP7te_MknrCaSuDiw.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1RD1nSel5cnW4SCXyF6DgdQ.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1RD1nSel5cnW4SCXyF6DgdQ.webp new file mode 100644 index 0000000000..c90681443b Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1RD1nSel5cnW4SCXyF6DgdQ.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1Tm7AYGZ0eWWjoCBdK0g1Nw.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1Tm7AYGZ0eWWjoCBdK0g1Nw.webp new file mode 100644 index 0000000000..c97b5657d1 Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1Tm7AYGZ0eWWjoCBdK0g1Nw.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1WQJfcFmwYRGKC1GO08om7g.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1WQJfcFmwYRGKC1GO08om7g.webp new file mode 100644 index 0000000000..d7e81c4b78 Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1WQJfcFmwYRGKC1GO08om7g.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1ZM5_Z6YnyhZitz3OOo00SA.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1ZM5_Z6YnyhZitz3OOo00SA.webp new file mode 100644 index 0000000000..d6bf899d98 Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1ZM5_Z6YnyhZitz3OOo00SA.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1dAgFBIxj5IoU17UZxc_L7Q.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1dAgFBIxj5IoU17UZxc_L7Q.webp new file mode 100644 index 0000000000..038d2be591 Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1dAgFBIxj5IoU17UZxc_L7Q.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1hizJxGODTTG7ufXYp75Ryw.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1hizJxGODTTG7ufXYp75Ryw.webp new file mode 100644 index 0000000000..b7f9d4ceeb Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1hizJxGODTTG7ufXYp75Ryw.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1i2m8A3zAOgMPIA4LstifYg.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1i2m8A3zAOgMPIA4LstifYg.webp new file mode 100644 index 0000000000..a2884a5179 Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1i2m8A3zAOgMPIA4LstifYg.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1opmQwD9KqK0Nom7JTgdPiQ.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1opmQwD9KqK0Nom7JTgdPiQ.webp new file mode 100644 index 0000000000..c061674b8d Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1opmQwD9KqK0Nom7JTgdPiQ.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1tQtclSgIB3HcSFOa6MYAxQ.webp b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1tQtclSgIB3HcSFOa6MYAxQ.webp new file mode 100644 index 0000000000..e930eef508 Binary files /dev/null and b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/images/1tQtclSgIB3HcSFOa6MYAxQ.webp differ diff --git a/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/index.md b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/index.md new file mode 100644 index 0000000000..6a1f703b92 --- /dev/null +++ b/sites/www/content/blog/firebase-crashlytics-and-the-flutter-casual-games-toolkit/index.md @@ -0,0 +1,216 @@ +--- +title: "Firebase Crashlytics and the Flutter Casual Games Toolkit" +description: "Learn how to enable Crashlytics in the Flutter Games example to make detecting and fixing issues in your game easier" +publishDate: 2022-08-03 +author: rich_hall +image: images/1B5i6MSUhz554dVTR484AuA.webp +category: spotlight +layout: blog +--- + +The [Flutter Casual Games Toolkit](https://docs.flutter.dev/resources/games-toolkit) (FCGT) is a starter template for developers who want to create a mobile game app. It has already been customized with a main menu, game levels, a settings screen, sound effects, music, and more. In addition to the template app, you can also download a [TicTacToe app](https://github.com/filiph/tictactoe) that is based on this template. + +The following screenshots show the out-of-the-box main menu, game level page, and a settings page, in that order: + +<DashImage figure src="images/1B5i6MSUhz554dVTR484AuA.webp" alt="Screenshots of the main FCGT screens" caption="Screenshots of the main FCGT screens" /> + + +Included in the FCGT are integrations with some useful services when developing your own game: the Apple Game Center, Google Play Games Services, the Google Mobile Ads SDK, and the In-App purchase package. These more advanced integrations are initially disabled because they require some setup by you, the developer, before you can use them. If you take a look at the game template’s [`main.dart`](https://github.com/flutter/samples/blob/main/game_template/lib/main.dart) file on GitHub and scroll down to the first “TODO”, you’ll see the code for the Ads, Games Services, and In-App Purchases plugins, lying dormant, waiting to be enabled. + +<DashImage figure src="images/1i2m8A3zAOgMPIA4LstifYg.webp" alt="Disabled Ads, Games Services, and In-App Purchases code" caption="Disabled Ads, Games Services, and In-App Purchases code" /> + + +Also included in the FCGT is Firebase Crashlytics, which is a powerful crash reporting solution that helps you detect, track, prioritize, and fix issues in real time. The Crashlytics dashboard allows you to visualize issues in your code, see how many users are affected, and figure out which lines of code are causing them. + +<DashImage figure src="images/1LcMq0sqB0w-n-3C-Zr3V-w.webp" alt="Firebase Crashlytics Dashboard" caption="Firebase Crashlytics Dashboard" /> + + +In its initial state, without Crashlytics enabled, the code in the **crashlytics/crashlytics.dart** file gathers error messages, logs, and stack traces and writes them to the console. This information is only available when testing on local development devices. With Crashlytics enabled, you can receive data from users running your production code on their own devices and view it on the Crashlytics dashboard. + +This article shows you how to enable Firebase Crashlytics in the FCGT, throw some test exceptions, and demonstrate how the exceptions are represented on the Crashlytics dashboard. If you’re interested in using Crashlytics for your Flutter App but don’t want to use the FCGT to develop a game, the information about throwing exceptions and the Crashlytics dashboard applies to any app hosted on Firebase. + +At the top of the [`main.dart file`](https://github.com/flutter/samples/blob/main/game_template/lib/main.dart), you can see the code for the Crashlytics integration: + +<DashImage figure src="images/1dAgFBIxj5IoU17UZxc_L7Q.webp" alt="main.dart: disabled Firebase Crashlytics code" caption="main.dart: disabled Firebase Crashlytics code" /> + + +This Crashlytics Firebase code wasn’t included in the original FCGT YouTube tutorial, [Quick start to building a game in Flutter](https://youtu.be/zGgeBNiRy-8?t=696) — this code will be explained soon. + +The steps to enable Crashlytics in the FCGT are listed in the [Crashlytics section of the FCGT `README.md` file](https://github.com/flutter/samples/tree/main/game_template#crashlytics), but keep reading for a walk through of each step and a demonstration of how things appear in the Crashlytics dashboard. + +To get started, use the following instructions: + +1. Download a copy of the latest version of the game template from the [Flutter project GitHub page](https://github.com/flutter/samples/tree/main/game_template). + +2. Ensure that you have Flutter installed on your computer and that you’re able to run apps on the iOS Simulator or Android Emulator. If you have any questions about how to do this, refer to the [Flutter Get started guide](https://docs.flutter.dev/get-started/install) for your target platform. + +3. Open the game template folder in your IDE or terminal and upgrade to the latest version of Flutter: + +``` +$> flutter upgrade +``` + + +4. Select the device or simulator where you’d like to run the app. + +5. Run the app: + +``` +$> flutter run +``` + + +If everything goes well, the app launches and you see the home screen: + +<DashImage figure src="images/16-qalTHst8nv6KOohptX1Q.webp" alt="Game template home screen" caption="Game template home screen" /> + + +The FCGT was designed to give developers a quick and comfortable foundation on which to build games. While you have the app running, navigate around and see what it can do. You’ll quickly notice that “game play” in the FCGT isn’t the most exciting (unless you’re a fan of sliders). The shallow structure of the game code is a deliberate design choice to keep things simple for when you start writing your own game. + +<DashImage figure src="images/1JZTY90OF41GsrvE6hFAZ3g.webp" alt="FCGT “game play”" caption="FCGT “game play”" /> + + +Now that you have the code, go to the [Firebase website](https://firebase.google.com/) and set up a free account (if you don’t have one). Then, open the [Firebase console](https://console.firebase.google.com/) and click **Add project**. From there, creating a new project is a 3-step process, just follow the on-screen instructions. You will be asked if you would like to enable Google Analytics (GA). It’s recommend that you do enable Google Analytics because, aside from being free, it unlocks some [powerful analytics functionality](https://firebase.google.com/docs/analytics), such as crash-free user data, session breadcrumbs, Velocity alerts, and BigQuery integration. + +If you enabled Google Analytics, install the Google Analytics Flutter plugin by using the following command: + +``` +$> flutter pub add firebase_analytics +``` + + +To learn more, refer to steps 1 and 2 in the [Use Google Analytics to get metrics for crash reports](https://firebase.google.com/docs/crashlytics/start-using-analytics?platform=flutter) guide, but there is no need to follow the additional steps outlined in the guide, as they are covered below. + +Next, install the Firebase CLI. Go to the [Firebase CLI docs](https://firebase.google.com/docs/cli), select your operating system, and follow the instructions. Once you’ve logged in, run the following command in your terminal and it should display a list of Firebase project names: + +``` +$> firebase projects:list +``` + + +Now, install the FlutterFire CLI which is dependent on the Firebase CLI, so don’t mix these steps up. To do this, follow the FlutterFire [CLI installation](https://firebase.flutter.dev/docs/cli/#installation) instructions. + +Using your terminal, navigate to the root folder of the game template project. You’ll know you’re in the right directory if you can see the pubspec.yaml file. + +Now run this command: + +``` +$> flutterfire configure +``` + + +Select the name of the Firebase project you created earlier. Then, select which target platforms your app supports. Currently, the Crashlytics console only supports iOS and Android, so select those two, at most. + +<DashImage figure src="images/1WQJfcFmwYRGKC1GO08om7g.webp" alt="Output from FlutterFire config command" caption="Output from FlutterFire config command" /> + + +The FlutterFire CLI generates all the keys and client IDs needed to connect to Firebase and overwrites them in the `src/firebase_options.dart` file. If you add more plugins or would like to enable more platforms in the future, simply re-run the `flutterfire configure` command. + +To enable Crashlytics, go back to the `lib/main.dart` file and uncomment the Crashlytics code. At the top of the file, there are 3 import statements that also must be uncommented: + +<DashImage figure src="images/1hizJxGODTTG7ufXYp75Ryw.webp" alt="Uncommented Crashlytics import statements" caption="Uncommented Crashlytics import statements" /> + + +Then, lower down in the same file, uncomment the Crashlytics integration code you looked at before. This code initializes an instance of Crashlytics if one doesn’t already exist, then it checks to ensure the app is running on either Android and iOS. The Crashlytics code won’t execute if the app is running on desktop or the web because the Crashlytics dashboard doesn’t yet support these platforms. + +<DashImage figure src="images/1opmQwD9KqK0Nom7JTgdPiQ.webp" alt="Crashlytics integration code" caption="Crashlytics integration code" /> + + +After you’ve uncommented the Crashlytics integration code, use the `flutter run` command to run the app again to make sure that everything still works. + +It’s time to crash your app! + +Actually, you’ll only be raising a few test exceptions but you’ll also learn how to locate them in the Crashlytics dashboard. This is useful because an uncaught exception might not cause your app to crash, but can still cause a poor user experience and, without Crashlytics enabled, they could go unreported. The [Crashlytics Fatal Error APIs](https://pub.dev/documentation/firebase_crashlytics/latest/firebase_crashlytics/FirebaseCrashlytics/recordFlutterFatalError.html) used in the `crashlytics/crashlytics.dart`file report uncaught exceptions as fatal events so they will appear in the Crashlytics dashboard and can be properly prioritized. + +<DashImage figure src="images/1ZM5_Z6YnyhZitz3OOo00SA.webp" alt="Crashlytics fatal error API" caption="Crashlytics fatal error API" /> + + +To learn more about how this logging is implemented, check out the guide on how to [Customize your Firebase Crashlytics Crash Reports](https://firebase.google.com/docs/crashlytics/customize-crash-reports?platform=flutter) and read the comments in the `crashlytics.dart` file. + +In your text editor or IDE of choice, navigate to the `main_menu` folder and open the `main_menu_screen.dart` file. Scroll down until you find the code for the **Settings** button. + +<DashImage figure src="images/1tQtclSgIB3HcSFOa6MYAxQ.webp" alt="Before (original code)" caption="Before (original code)" /> + + +Make a copy of the `ElevatedButton` and then, inside the `onPressed` call-back, instead of calling `GoRouter` to navigate to the **Settings** page, throw an exception. While you’re there, change the name of the button to something like **Throw Test Exception** as shown below: + +``` +ElevatedButton( + onPressed: () => GoRouter.of(context).go('/settings'), + child: const Text('Settings'), +), +_gap, +ElevatedButton( + onPressed: () => throw Exception(), + child: const Text("Throw Test Exception"), +), +``` + + +Next, navigate to the `audio` folder and open the `audio_controller.dart` file. Scroll down until you find the `_musicOnHandler` function. The framework calls this function when the user toggles the music play on the **Settings** screen. + +<DashImage figure src="images/18MiFLAgG8cCpTtqTeX1v8Q.webp" alt="Before (original code)" caption="Before (original code)" /> + + +Inside the `else` statement, below where the `_stopMusic()` function is called, use the `_log.severe()` method to write a message to the console. Any log message with `Level.severe` or above is sent to Crashlytics, so this message will appear in the Crashlytics console, too. For demonstration purposes, throw another exception here as well: + +``` +void _musicOnHandler() { + if (_settings!.musicOn.value) { + // Music got turned on. + if (!_settings!.muted.value) { + _resumeMusic(); + } + } else { + // Music got turned off. + _stopMusic(); + _log.severe("Someone is messing with the music!"); + throw Exception(); + } +} +``` + + +Now there are two places in the app where you can click a button and throw an exception. Run your app again and verify that there’s a **Throw Test Exception** button in the Main Menu. + +<DashImage figure src="images/1DOGxw3bdrfqppiG2ZobhIQ.webp" alt="Main menu with throw exception button" caption="Main menu with throw exception button" /> + + +Clicking the **Throw Test Exception** button generates a full stack trace as of when the exception was thrown and writes it to the console: + +<DashImage figure src="images/1RD1nSel5cnW4SCXyF6DgdQ.webp" alt="Console output after an exception was thrown" caption="Console output after an exception was thrown" /> + + +If you click **Settings** to open the settings view and then tap the musical note button a couple of times, more exceptions and stack traces will appear in your console. Now that you have Crashlytics enabled, this data is available in the Crashlytics dashboard. + +Open the [Firebase console](https://console.firebase.google.com/project/_/crashlytics), select your project, and then select **Crashlytics** from the menu on the left. Choose the version of the project you are running (Android or iOS) and you should be able to see the Crashlytics dashboard. + +<DashImage figure src="images/1LcMq0sqB0w-n-3C-Zr3V-w.webp" alt="Firebase Crashlytics Dashboard" caption="Firebase Crashlytics Dashboard" /> + +> If, after a couple of minutes, all you see when you try to access the Crashlytics dashboard, is this guy swishing around at bugs, it’s probably because Crashlytics hasn’t detected any events yet. +> In this case, stop your app from running and then go back to your device or simulator, close the app, and start it again by tapping on the app icon. Now, try throwing some exceptions in the app and check the Crashlytics dashboard again. If you’ve refreshed the Firebase console, restarted your app manually, and you’re still not seeing the test crash after five minutes, [enable debug logging](https://firebase.google.com/docs/crashlytics/test-implementation#enable-debug-logging) to see if your app is sending crash reports. + +<DashImage figure src="images/1OEZMrFP7te_MknrCaSuDiw.webp" alt="No events detected by Crashlytics yet" caption="No events detected by Crashlytics yet" /> + + +If you’ve enabled Google Analytics, the crash-free statistics pane will show you the percentage of users who have not experienced a crash during a selected time range. + +<DashImage figure src="images/14KjDPJFvTyV_8eRVSwug5w.webp" alt="Crashlytics dashboard crash-free statistics pane" caption="Crashlytics dashboard crash-free statistics pane" /> + + +The **Issues** table of the bottom of the Crashlytics dashboard displays the thrown exceptions. + +<DashImage figure src="images/1Tm7AYGZ0eWWjoCBdK0g1Nw.webp" alt="Crashlytics dashboard issues table" caption="Crashlytics dashboard issues table" /> + + +Clicking on a particular issue in the table shows a card for every time the issue has occurred, along with more detailed information about the issue. By default, the stack trace is shown, which allows you to identify where in your code the crash happened. As you can see in the screenshot below, the exception was thrown from the `_musicOnHandler()` **function on line 197 of the `audio_controller.dart` file. This is no surprise because that’s exactly where you placed that exception in the code, but imagine how useful this is for tracking down issues in complex production apps. + +<DashImage figure src="images/15pbF1OIADrJvldPPBOjTYg.webp" alt="Crashlytics dashboard stack trace" caption="Crashlytics dashboard stack trace" /> + + +If you click on the **Logs** tab, you can see any messages written to the console by the `_log.severe()` method: + +<DashImage figure src="images/13l-v0ElBcVHNAEwhei1ZkA.webp" alt="Crashlytics dashboard logs" caption="Crashlytics dashboard logs" /> + + +Congratulations!! Now, when you go on to write an award-winning game (maybe with lots of sliders in it), you’ll have Crashlytics enabled to help you find and fix any issues. \ No newline at end of file diff --git a/sites/www/content/blog/flutter-1-0-launch-wrap-up/images/0kS0qvDFLKFUHsmKl.webp b/sites/www/content/blog/flutter-1-0-launch-wrap-up/images/0kS0qvDFLKFUHsmKl.webp new file mode 100644 index 0000000000..40a82b6d75 Binary files /dev/null and b/sites/www/content/blog/flutter-1-0-launch-wrap-up/images/0kS0qvDFLKFUHsmKl.webp differ diff --git a/sites/www/content/blog/flutter-1-0-launch-wrap-up/images/14_hWh5hMP6F3W6DZ-6OK2Q.webp b/sites/www/content/blog/flutter-1-0-launch-wrap-up/images/14_hWh5hMP6F3W6DZ-6OK2Q.webp new file mode 100644 index 0000000000..cfec91fff1 Binary files /dev/null and b/sites/www/content/blog/flutter-1-0-launch-wrap-up/images/14_hWh5hMP6F3W6DZ-6OK2Q.webp differ diff --git a/sites/www/content/blog/flutter-1-0-launch-wrap-up/index.md b/sites/www/content/blog/flutter-1-0-launch-wrap-up/index.md new file mode 100644 index 0000000000..184de62c45 --- /dev/null +++ b/sites/www/content/blog/flutter-1-0-launch-wrap-up/index.md @@ -0,0 +1,76 @@ +--- +title: "Flutter 1.0 Launch Wrap-up" +description: "A few reflections on Flutter Live, our global celebration of the Flutter 1.0 launch." +publishDate: 2018-12-28 +author: csells +image: images/14_hWh5hMP6F3W6DZ-6OK2Q.webp +category: releases +layout: blog +--- + +It’s been a couple of weeks now since Flutter Live, our global celebration of Flutter, and we thought we’d share a few reflections on the event. The Flutter team was blown away by the community excitement around [the release of Flutter 1.0](https://developers.googleblog.com/2018/12/flutter-10-googles-portable-ui-toolkit.html). Flutter lets you build fast, beautiful apps using tools that work with your favorite IDE/editor and an open source runtime that makes you a much more productive developer. The event was a wonderful moment to share our plans for Flutter, demonstrate some of the amazing ways partners are building on Flutter, and see Flutter community groups come together around the world. + +## Flutter Momentum + +Since [Flutter entered beta in February](https://medium.com/flutter-io/announcing-flutter-beta-1-build-beautiful-native-apps-dc142aea74c0) of this year, there have been apps built for more than 200 million users by 250,000+ developers. + +In that short time, Flutter’s repository has already reached the top 25 on GitHub for active software repos. At Flutter Live, we shared that Flutter was the 34th most popular active software repo on GitHub, but since the event, it’s already moved to 21, ahead of Swift, Kubernetes, Angular and TypeScript. We continue to be humbled and amazed by the affection that developers have for Flutter. + +And it’s clear that developers are using Flutter to build great things. Looking at apps like [Reflectly](https://reflectly.app/), [Topline from Abbey Road](https://youtu.be/_ACWeGGBP4E) and the brand new [History of Everything](https://medium.com/2dimensions/the-history-of-everything-981d989e1b45), you can feel how amazing they are in every pixel and in every motion. You can see that users agree by looking at the Google Play and Apple App Store ratings for these and [other popular Flutter apps](https://flutter.io/showcase). + +The momentum rolled right into the Flutter Live event, as you can see here in the lead up to the keynote. + +<YoutubeEmbed id="DpBWkYiSBtQ" title="Flutter Live Event Guides Highlights" fullwidth="true"/> + + +All of that was in preparation for the event itself, which is recapped in this 10-minute highlight reel. + +<YoutubeEmbed id="D-o4BqJxmJE" title="Flutter Live Keynote Recap" fullwidth="true"/> + + +And if you haven’t seen it already, you can check out [the full playlist of content from Flutter Live](https://www.youtube.com/playlist?list=PLOU2XLYxmsILq4ysYNWXq5TOGLgYDJgVD). + +## Flutter Community + +Reaching Flutter 1.0 was not just a Google effort, but a whole community effort. The Flutter community has been invaluable in the development of Flutter and the related tooling, with more than 12,000 commits by 285 contributors. Flutter was developed in the open to encourage global participation, but the team never expected how strong and how quickly the community would grow. Little known fact: the development of the [Visual Studio Code plug-in for Flutter](https://dartcode.org/) was done by a single community developer: Danny Tuppeny (aka [DanTup](https://github.com/DanTup)). Danny did such an amazing job that now making Flutter work great in VSCode is his full-time job! + +The inspiration that Flutter has on developers doesn’t stop there. Developers gathered en masse in viewing parties around the world in places like Estonia: + +<YoutubeEmbed id="PcrIkshxnjo" title="Nevercode launches Codemagic - Flutter Live & Launch Party Aftermovie - December 4th, 2018" fullwidth="true"/> + + +China: + +<YoutubeEmbed id="w68o_B76YlI" title="Flutter Live Beijing Sizzle Reel" fullwidth="true"/> + + +And Kampala, Uganda: + +<DashImage figure src="images/0kS0qvDFLKFUHsmKl.webp" /> + + +And thanks to all of the developers that posted to #MyFlutterStory! It was hard to pick the best contributions. + +<YoutubeEmbed id="9WgQzB0UZOs" title="#MyFlutterStory - Stories From Developers Using Flutter" fullwidth="true"/> + + +These developers haven’t been idle. Looking at [the list of Flutter packages](https://pub.dartlang.org/packages?q=dependency%3Aflutter) on pub.dartlang.org shows over 1000 community-contributed packages that you can drop into your projects today. You can see a small sample of what the community is already building using these resources today: + +<YoutubeEmbed id="REJDzio_h7o" title="Flutter App Highlight Reel" fullwidth="true"/> + + +But that’s not all — in addition to the existing set of packages to [add Firebase to your Flutter app](https://firebaseopensource.com/projects/flutter/plugins/), there’s also [the new Firebase MLKit](https://pub.dartlang.org/packages/firebase_ml_vision), which enables real-time image and feature recognition, and [the two new Square SDKs for Flutter](https://squareup.com/us/en/flutter) that allows you to take payments from a Square Reader or directly inside your app. + +On the tooling side, you’re probably already familiar with the Flutter plugins for [Android Studio](https://flutter.io/docs/get-started/editor?tab=androidstudio) and [Visual Studio Code](https://flutter.io/docs/get-started/editor?tab=vscode), but did you know about [2Dimensions Flare](https://medium.com/2dimensions/flare-launch-d524067d34d8), which helps you build great vector animations for Flutter, or [Nevercode Codemagic](https://nevercode.io/blog/why-we-built-codemagic/), which provides testing and continuous deployment to the Google Play and Apple App Store for your Flutter app? + +It’s clear that the Flutter community is strong and it’s getting stronger. To get started, you’ll find an excellent set of docs, samples, videos and codelabs provided on the Flutter site at [flutter.io](http://flutter.io). And when you’ve got questions about Flutter development, issues to report or features to request, [become a part of the Flutter community](https://flutter.io/community). + +## Flutter Future + +With the release of Flutter 1.0 that targets Android and iOS, the team is just getting started. If fact, there are already new features in beta for you to try out. If you want to add existing mobile platform components to your Flutter app, you can do so via [platform views](https://medium.com/flutter-community/flutter-platformview-how-to-create-flutter-widgets-from-native-views-366e378115b6), which have in turn been used to create the new [Web View](https://pub.dartlang.org/packages/webview_flutter) and [Google Maps](https://pub.dartlang.org/packages/google_maps_flutter) packages. Conversely, if you’d like to drop Flutter code into your existing mobile apps, you can do so with [the Add to App feature](https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps), allowing you to leverage your current apps while still taking advantage of the power and beauty of Flutter. We’ll continue to add features to Flutter based on your [feedback](https://flutter.io/community). + +From the beginning, Flutter has been a UI toolkit for all screens, not just mobile. We have begun to showcase this by demonstrating a preview of ongoing work to support Flutter on [desktop](https://github.com/google/flutter-desktop-embedding) devices running Linux, Mac or Windows, along with giving a sneak peek at a project codenamed “[Hummingbird](https://medium.com/flutter-io/hummingbird-building-flutter-for-the-web-e687c2a023a8)”, which lets you compile Flutter code to run on the standards-based web. These are experimental projects and we’re looking forward to putting this into your hands so that we can get your feedback. + +So while we’ve got lots planned for Flutter in the future, it’s ready for you right now. What will you build? + +<DashImage figure src="images/14_hWh5hMP6F3W6DZ-6OK2Q.webp" /> diff --git a/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/06ZkWSeMzkU_vy8uJ.webp b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/06ZkWSeMzkU_vy8uJ.webp new file mode 100644 index 0000000000..d7a41a5f96 Binary files /dev/null and b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/06ZkWSeMzkU_vy8uJ.webp differ diff --git a/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0BujM3j2SMg1qp3hi.webp b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0BujM3j2SMg1qp3hi.webp new file mode 100644 index 0000000000..b278ceb86e Binary files /dev/null and b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0BujM3j2SMg1qp3hi.webp differ diff --git a/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0EvfHfmRcMbEyHcAh.webp b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0EvfHfmRcMbEyHcAh.webp new file mode 100644 index 0000000000..33c930b262 Binary files /dev/null and b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0EvfHfmRcMbEyHcAh.webp differ diff --git a/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0GfXUuto3tB79I47F.webp b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0GfXUuto3tB79I47F.webp new file mode 100644 index 0000000000..ce35551297 Binary files /dev/null and b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0GfXUuto3tB79I47F.webp differ diff --git a/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0LreVRDXghbLuzKDR.webp b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0LreVRDXghbLuzKDR.webp new file mode 100644 index 0000000000..70ad48bc6c Binary files /dev/null and b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0LreVRDXghbLuzKDR.webp differ diff --git a/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0YjPyKLHdN6xW-4-3.webp b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0YjPyKLHdN6xW-4-3.webp new file mode 100644 index 0000000000..b63483c30e Binary files /dev/null and b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0YjPyKLHdN6xW-4-3.webp differ diff --git a/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0dQYszaV1ZKzV_b-y.webp b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0dQYszaV1ZKzV_b-y.webp new file mode 100644 index 0000000000..7044d72cc8 Binary files /dev/null and b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0dQYszaV1ZKzV_b-y.webp differ diff --git a/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0q-OVlB4QmxOfjfOM.webp b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0q-OVlB4QmxOfjfOM.webp new file mode 100644 index 0000000000..91302d9bd2 Binary files /dev/null and b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0q-OVlB4QmxOfjfOM.webp differ diff --git a/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0zeJT-J_oHhKYFcem.webp b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0zeJT-J_oHhKYFcem.webp new file mode 100644 index 0000000000..e90f03ebc2 Binary files /dev/null and b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/images/0zeJT-J_oHhKYFcem.webp differ diff --git a/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/index.md b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/index.md new file mode 100644 index 0000000000..5f052f7b39 --- /dev/null +++ b/sites/www/content/blog/flutter-2023-q1-survey-api-breaking-changes-deep-linking-and-more/index.md @@ -0,0 +1,108 @@ +--- +title: "Flutter 2023 Q1 survey — API breaking changes, deep linking, and more" +description: "The Flutter team has been running a quarterly user survey program since its 1.0 release in 2018. The 19th survey was announced in the…" +publishDate: 2023-06-30 +author: jayoung-lee +image: images/0BujM3j2SMg1qp3hi.webp +category: announcements +layout: blog +--- + +## Flutter 2023 Q1 survey — API breaking changes, deep linking, and more + +The Flutter team has been running a quarterly user survey program since its 1.0 release in 2018. The 19th survey [was announced](https://twitter.com/FlutterDev/status/1630674202297610240) in the first quarter of 2023, collecting 13,378 responses over the 14-day window. The survey intended to collect timely feedback from Flutter developers, and included questions about the following subjects: + +* Perceived growth of Flutter + +* API breaking changes + +* Deep linking + +Overall, 93% of respondents were positively satisfied (somewhat + very satisfied), with 55% being very satisfied. This number has been consistent since August 2022. It’s also notable that developers continued to be highly satisfied with Flutter, because the number of users increased by 24% over this period. + +## Perceived growth of Flutter + +In this section, we asked how respondents feel about Flutter’s recent growth. We believe that developers’ perception of Flutter’s growth influences their decision to continue using it, and we wanted to know if our perception matches that of our users. + +When we asked the respondents to rate the recent growth, 80% said that Flutter’s growth in popularity is fast, as shown in the following graph. This matches our data, which shows that the number of monthly active users has increased by 24% over the last 3 quarters, as previously mentioned. + +<DashImage figure src="images/0EvfHfmRcMbEyHcAh.webp" alt="*42% said that Flutter’s growth is very fast and 38% said that it is somewhat fast*" caption="*42% said that Flutter’s growth is very fast and 38% said that it is somewhat fast*" /> + + +The Flutter team’s improvements are a major factor (63%) in the perceived rapid growth of Flutter, according to 80% of respondents who selected “fast” in the previous question (N=2877). Other notable factors include learning resources (57%), tools and services that work with Flutter (51%), and global online communities (50%). It was reassuring to learn that the areas where the Flutter team can improve also contribute to the perceived growth. We will continue introducing new videos, collaborating with other teams to improve tools, and supporting healthy communities. + +Of those who said that the growth of the technology is slow (4%, or 137 people), the most common reasons given were a lack of job postings (66%) and adoption by well-known companies (55%). Flutter’s Developer Relations team found that some hiring managers are having trouble finding qualified candidates, and job seekers are having trouble improving their skills to meet the requirements of hiring companies. To bridge this gap, the Flutter team is planning to work on more resources to help Flutter developers improve their skills while working towards a job. We will also continue to share [developer stories](https://youtu.be/bHg6TzuP9bc), so you can see how others are using Flutter for their production apps. + +<DashImage figure src="images/0zeJT-J_oHhKYFcem.webp" alt="*Reasons to think the Flutter’s recent growth is fast or slow*" caption="*Reasons to think the Flutter’s recent growth is fast or slow*" /> + + +## API breaking changes + +Breaking changes refer to changes that introduce new behavior incompatible with previous versions of the API. Breaking changes help us keep the API clean and consistent, but developers need to update their previously written code to work with the new behavior, whether the change is major like the introduction of null-safety in Dart, or minor like the deprecation of `GestureRecognizer.kind()`. + +To reduce the pain associated with the process, Flutter has been announcing breaking changes in various places (such as [the Flutter website](https://docs.flutter.dev/release/breaking-changes), and the [Flutter Announce](https://groups.google.com/forum/#!forum/flutter-announce) or [Dart Announce](https://groups.google.com/a/dartlang.org/g/announce) Google groups). In each stable release, tooling is updated to display warning messages when appropriate and migration tools are provided where possible (Flutter Fix, `dart fix` command). A Flutter developer 2019 survey questioned developers how they felt about breaking changes in order to ensure a cleaner API. Overall, developers liked the idea, but we were asked to keep the following in mind: + +* Change slowly + +* Offer a good migration tool + +* Provide documentation, examples, tutorials, change logs + +* Clearly explain the benefit + +* Broadly announce it + +Because Flutter has experienced significant growth since then (both in number of developers and size of API), we wanted to survey you again and decide whether an update to our policy was necessary. + +Overall, the proportion of developers who have been affected by breaking changes in the last 12 months increased from 30% to 51%, over the last 4 years. + +<DashImage figure src="images/0BujM3j2SMg1qp3hi.webp" alt="*51% of the developers have experienced breaking changes in the last 12 months*" caption="*51% of the developers have experienced breaking changes in the last 12 months*" /> + + +44% of respondents affected by breaking changes find it frustrating that they have to keep updating their code, which was also higher than 2019 (32%). However, the majority of you agree that the changes made the code cleaner (67%), compared to 2019 (47%). Also, the majority of respondents agree that the changes were valuable (63%), which increased from 49% in 2019. + +This suggests that the complexity of the changes we introduced appears to have increased, but we were successful in having the changes deliver value to developers. Given that overall 60% of the respondents think that breaking changes have more benefits than annoyances (whereas 11% hold the opposite view), we learned that the changes have been largely beneficial to the Flutter developers. + +<DashImage figure src="images/0YjPyKLHdN6xW-4-3.webp" alt="*Perception around breaking changes*" caption="*Perception around breaking changes*" /> + + +We also asked how satisfied you are with how breaking changes are handled. We learned that satisfaction rates were consistent across the various aspects of our efforts. + +<DashImage figure src="images/0LreVRDXghbLuzKDR.webp" alt="*Ratings on the Flutter team’s handling of breaking changes*" caption="*Ratings on the Flutter team’s handling of breaking changes*" /> + + +In light of this, we have decided to double down on our current policy. We plan to find more ways to automate migrations, improve the discoverability of our documentation, and more broadly communicate the value and reasoning behind the changes we do make. + +We expect to revisit this again in a few years. As Flutter matures, we believe it’s likely that developers will shift from valuing the improvements introduced in breaking changes to valuing the stability of an API that never breaks. We want to make best use of the time where our community understands the value of breaking changes but not frustrate the Flutter developer base if this attitude changes. + +## Deep linking + +Deep links send users directly to an in-app location instead of a website or an app store location. Since this is a useful feature for developers, we wanted to measure the awareness of this feature, understand how developers are implementing the feature, and assess how easy it is to set up deep links, so that we can support this feature better. + +First of all, we learned that deep links are known to about half of the respondents (55%). Among these, about 50% tried implementing deep links in their Android app, and about 39% tried implementing them in their iOS app, as displayed in the following graph. In both platforms, less than 5% reported that they had trouble implementing the feature. + +<DashImage figure src="images/0dQYszaV1ZKzV_b-y.webp" alt="*Percentage of developers who implemented deep links in Android and iOS apps*" caption="*Percentage of developers who implemented deep links in Android and iOS apps*" /> + + +To implement deep links for your app, you need to configure a web portion and an app portion. The web portion directs users to the app, and the app portion then directs users to the correct page. + +When it comes to the technology that developers chose to set up their web, about half of respondents said that they use Firebase Dynamic Links. However, we recently learned from the Firebase team that Firebase Dynamic Links is no longer recommended for new projects. The Dynamic Links service will be discontinued in favor of native files (App Links for Android and Universal Links for iOS). You will have at least 12 months to migrate from the date Firebase announces the shutdown. For more information, check out the [Dynamic Links FAQ](https://firebase.google.com/support/dynamic-links-faq). + +<DashImage figure src="images/0GfXUuto3tB79I47F.webp" /> + + +<DashImage figure src="images/0q-OVlB4QmxOfjfOM.webp" alt="What developers use to guide users from their websites to an iOS or Android app" caption="What developers use to guide users from their websites to an iOS or Android app" /> + + +For app setup, users have adopted various approaches. Named routes, which is the simplest solution, was the most popular choice (31%). It was followed by the `go_router` package, which is a better solution if you want the features that named routes don’t support, such as route stack management. These features can be particularly useful when you need more control over your deep links. + +<DashImage figure src="images/06ZkWSeMzkU_vy8uJ.webp" alt="What developers use to route users within an app" caption="What developers use to route users within an app" /> + + +From the survey, we learned that we should put more effort into explaining the value of the feature so that more developers can benefit from it, and make it easier to implement. We are planning to launch a validation tool in the coming quarters to streamline the implementation process. + +## What’s next? + +The Flutter team is always eager to hear from you and incorporate your feedback into the engineering roadmap. This quarter’s survey was particularly valuable because it gave us the confidence to continue our breaking change policy and helped us identify blind spots around deep linking. We will continue to listen to you and incorporate your feedback into our work. + +The Flutter team is committed to providing the best possible experience for developers. We believe that with your input, we can create a framework that is both powerful and developer-friendly. We appreciate your continued support and look forward to hearing from you in the future. See you in the next survey! \ No newline at end of file diff --git a/sites/www/content/blog/flutter-2025-roadmap-update/images/0sO8Aqa-_eHZ9jcZz.webp b/sites/www/content/blog/flutter-2025-roadmap-update/images/0sO8Aqa-_eHZ9jcZz.webp new file mode 100644 index 0000000000..e1af6bd1cd Binary files /dev/null and b/sites/www/content/blog/flutter-2025-roadmap-update/images/0sO8Aqa-_eHZ9jcZz.webp differ diff --git a/sites/www/content/blog/flutter-2025-roadmap-update/index.md b/sites/www/content/blog/flutter-2025-roadmap-update/index.md new file mode 100644 index 0000000000..22d1127e31 --- /dev/null +++ b/sites/www/content/blog/flutter-2025-roadmap-update/index.md @@ -0,0 +1,24 @@ +--- +title: "Flutter 2025 roadmap update" +description: "Transparency is a core goal of the Flutter open source project, with everything from issues to design specifications being shared in the…" +publishDate: 2025-04-02 +author: mit-mit +image: images/0sO8Aqa-_eHZ9jcZz.webp +category: announcements +layout: blog +--- + +Transparency is a core goal of the Flutter open source project, with everything from issues to design specifications being shared in the open. One of the most highly requested pieces of information is the yearly roadmap for the core Flutter team at Google, and today we’re happy to share the [2025 roadmap](https://github.com/flutter/flutter/blob/master/docs/roadmap/Roadmap.md). + +We’ve been publishing our roadmaps since 2020, and this is a natural continuation of previous years’ work; we’re still working towards our long-term goal of creating the most popular, fastest growing, and highest-productivity multi-platform UI framework for building rich app experiences! + +For all the details, check out the freshly updated [Flutter roadmap on GitHub](https://github.com/flutter/flutter/blob/master/docs/roadmap/Roadmap.md). + +<DashImage figure src="images/0sO8Aqa-_eHZ9jcZz.webp" /> + + +As with any other software product roadmap, note that plans tend to shift and adapt throughout the year, so don’t be surprised if some changes happen along the way. But we still feel it’s important to share our overall plans. + +Also note that what we’re listing here is primarily content gathered from those of us who work on Flutter as employees of Google. By now non-Google contributors outnumber those employed by Google, so this is not an exhaustive list of all the new and exciting things that we hope will come to Flutter this year! + +We remain greatly thankful for the community and your continued support. We can’t wait to see what you’ll build! \ No newline at end of file diff --git a/sites/www/content/blog/flutter-and-dart-at-google-cloud-next/images/0mlHluTwnXfWI-Yzy.webp b/sites/www/content/blog/flutter-and-dart-at-google-cloud-next/images/0mlHluTwnXfWI-Yzy.webp new file mode 100644 index 0000000000..89b4a9823b Binary files /dev/null and b/sites/www/content/blog/flutter-and-dart-at-google-cloud-next/images/0mlHluTwnXfWI-Yzy.webp differ diff --git a/sites/www/content/blog/flutter-and-dart-at-google-cloud-next/index.md b/sites/www/content/blog/flutter-and-dart-at-google-cloud-next/index.md new file mode 100644 index 0000000000..ebd5c906ba --- /dev/null +++ b/sites/www/content/blog/flutter-and-dart-at-google-cloud-next/index.md @@ -0,0 +1,58 @@ +--- +title: "Flutter and Dart at Google Cloud Next" +description: "In case you missed it, the Flutter team attended Google Cloud Next for the first time this past week, a testament to Flutter’s growing…" +publishDate: 2024-04-16 +author: kkboateng +image: images/0mlHluTwnXfWI-Yzy.webp +category: events +layout: blog +--- + +In case you missed it, the Flutter team attended [Google Cloud Next](https://cloud.withgoogle.com/next) for the first time this past week, a testament to Flutter’s growing impact within the development community. + +<DashImage figure src="images/0mlHluTwnXfWI-Yzy.webp" /> + + +We partnered with teams across Google Cloud, Firebase, [Very Good Ventures](https://verygood.ventures/), and [Serverpod](https://serverpod.dev/) to deliver lightning talks, sessions, and a demo booth with a focus on streamlined workflows. + +Here’s a review of what we shared in case you missed it. + +**Fast Development with Firestore, Cloud Run, and Flutter** +We worked in real-time to develop a minimal viable Flutter app, backed by a Firestore database through a Google Cloud Run-powered API. You can learn more on how to do this yourself from these [resources](https://assets.swoogo.com/uploads/3784202-66185e868d8c1.pdf). + +**Harnessing Flutter, Firebase, and Gemini for Intelligent App Development** +We built a dynamic, real-world application from scratch using Flutter, Firebase, and Gemini, showing live how Firebase’s Firestore and Storage features streamlined data management and media handling. Watch a [recording of the talk.](https://youtu.be/gr890xePN10) + +**How to use Serverpod for Full-Stack Dart** +We showed how developers can use Dart on the server to share code and complement their Flutter app with [Serverpod](https://serverpod.dev/), a full-stack Dart solution that uses code generation to create matching client-server code and a feature-rich Postgres ORM based on your schema. Check out [this Observable Flutter episode](https://www.youtube.com/watch?v=0bGpPU2vBRY) to learn more. + +**Live-coding a Flutter app with Firestore** +We showed how Google’s [Project IDX ](https://idx.dev/)makes it much easier to set up a development environment for Flutter! Check out Project IDX to get started. + +**Developing full-stack apps and games with Flutter, Dart Frog, and Gemini** +Very Good Ventures (VGV) showed how to create an AI-powered game with Gemini and Flutter à la “choose your own adventure” style. Watch the [talk now!](https://www.youtube.com/watch?v=_NRDyKiVIa4) + +**How to use Dart Frog and Google Cloud to share code** +VGV showed how to get up and running quickly with [code sharing between your frontend and backend](https://dartfrog.vgv.dev/), bringing up a full-stack project and getting it running on both device and in Cloud Run in a matter of minutes. + +### In case you missed it + +Check out these announcements from Cloud Next that we think have the potential to help Flutter and Dart developers more easily build AI features in your apps. + +**Firestore Vector Search support + extension launch** +[Vector search embeddings in Firestore](https://cloud.google.com/blog/products/databases/get-started-with-firestore-vector-similarity-search), along with the [Firestore Vector Search extension](https://extensions.dev/extensions/googlecloud/firestore-vector-search), enable Flutter and Dart developers to turn Firestore data into a rich, searchable knowledge map, empowering highly innovative generative AI features. This opens the door to integrating AI-powered product recommendations, intelligent chatbots, and other sophisticated experiences directly into your Flutter applications when you use Firestore. + +**Vertex Gemini Dart SDK private preview** +The Vertex Gemini SDKs are a set of client SDKs for Firebase that support Dart, Kotlin, Swift, and JavaScript. They are meant to accelerate development with easy access to Vertex AI services. Flutter and Dart developers can request access to the private preview on this [Google form](https://docs.google.com/forms/d/e/1FAIpQLScIuowa37cp-yCCLOy9QAFYLVzBPXiU5x-mgENvVmn9OYLtYw/viewform). + +### **Show us what you’re building** + +The future is bright for developers who want to build high quality cross-platform experiences powered by Google Cloud, and we want to see what you’re building. If you use Flutter with Firebase or any Google Cloud products or services, give us a shout on [Twitter](https://twitter.com/FlutterDev) and [LinkedIn](https://www.linkedin.com/showcase/flutterdevofficial/)! + +If you’re just looking to get started with Flutter and Google Cloud, check out the following resources. + +[Documentation](https://dart.dev/server/google-cloud) +[Sample code](https://github.com/flutter/samples/tree/main/code_sharing) +[Cloud Development tips (Video)](https://www.youtube.com/watch?v=P1XzbpeD2GY) + +Until Next 😉 time — we can’t wait to see what you build! \ No newline at end of file diff --git a/sites/www/content/blog/flutter-and-desktop/images/0OLMhLy7-b-4BisTc.webp b/sites/www/content/blog/flutter-and-desktop/images/0OLMhLy7-b-4BisTc.webp new file mode 100644 index 0000000000..9bd3322899 Binary files /dev/null and b/sites/www/content/blog/flutter-and-desktop/images/0OLMhLy7-b-4BisTc.webp differ diff --git a/sites/www/content/blog/flutter-and-desktop/images/0iBeoGqAPyC66-BBe.webp b/sites/www/content/blog/flutter-and-desktop/images/0iBeoGqAPyC66-BBe.webp new file mode 100644 index 0000000000..8a4775f594 Binary files /dev/null and b/sites/www/content/blog/flutter-and-desktop/images/0iBeoGqAPyC66-BBe.webp differ diff --git a/sites/www/content/blog/flutter-and-desktop/images/0rCBxqUkuMqFvPvq4.webp b/sites/www/content/blog/flutter-and-desktop/images/0rCBxqUkuMqFvPvq4.webp new file mode 100644 index 0000000000..d3f5aa2324 Binary files /dev/null and b/sites/www/content/blog/flutter-and-desktop/images/0rCBxqUkuMqFvPvq4.webp differ diff --git a/sites/www/content/blog/flutter-and-desktop/images/1tCqp0X4-8_yDjhbtVbqx-w.webp b/sites/www/content/blog/flutter-and-desktop/images/1tCqp0X4-8_yDjhbtVbqx-w.webp new file mode 100644 index 0000000000..551c4e7b17 Binary files /dev/null and b/sites/www/content/blog/flutter-and-desktop/images/1tCqp0X4-8_yDjhbtVbqx-w.webp differ diff --git a/sites/www/content/blog/flutter-and-desktop/index.md b/sites/www/content/blog/flutter-and-desktop/index.md new file mode 100644 index 0000000000..8602c09bdf --- /dev/null +++ b/sites/www/content/blog/flutter-and-desktop/index.md @@ -0,0 +1,116 @@ +--- +title: "Flutter and Desktop" +description: "Ongoing progress towards an ambient computing vision" +publishDate: 2020-06-17 +author: timsneath +image: images/0OLMhLy7-b-4BisTc.webp +category: announcements +layout: blog +--- + +## Flutter and Desktop Apps + +It’s no secret that our mission for Flutter is to target a broad variety of devices— including iOS, Android, Windows, Linux, macOS, and web — from a single codebase, with native compilation and game-quality visuals. Within Google Flutter is used by projects from [Assistant](https://store.google.com/product/google_nest_hub) to [Stadia](https://stadia.google.com/), from [Cloud Search](https://play.google.com/store/apps/details?id=com.google.enterprise.topaz.mobile.android) to [Blogger](https://play.google.com/store/apps/details?id=com.google.android.apps.blogger). And outside of Google, Flutter has been adopted by companies from [ByteDance](https://apps.apple.com/cn/app/%E4%BB%8A%E6%97%A5%E5%A4%B4%E6%9D%A1/id529092160?l=en) to [Grab](https://play.google.com/store/apps/details?id=com.grabtaxi.passenger), from [Nubank](https://medium.com/building-nubank/https-medium-com-freire-why-nubank-chose-flutter-61b80b568772) to [MGM Resorts](https://superformula.com/flutter/), all of whom benefit from the productivity and flexibility of Flutter. + +Many of you are interested in progress on desktop operating systems including Windows, macOS, and Linux: in surveys and on GitHub, desktop is repeatedly one of the most popular new features. Over the coming weeks, we’re going to show off more of our work here, and we thought we’d start by surveying some of the work from various feature teams that contribute towards the overall project. While desktop support is currently a technical preview, there’s been plenty of work going on. + +<DashImage figure src="images/0OLMhLy7-b-4BisTc.webp" /> + + +## Release Mode + +Recently we checked in profile and [release mode for Windows](https://github.com/flutter/flutter/issues/38477) and [Linux](https://github.com/flutter/flutter/issues/38478), in addition to the existing support for macOS. For example, if you’re running the latest builds of Flutter, you can now compile a Flutter application to a Windows executable with `flutter build windows`. This uses our production [AOT compiler](https://en.wikipedia.org/wiki/Ahead-of-time_compilation) to create native x64 machine code that can be distributed to a machine without Flutter installed. + +## Desktop-class experiences + +Whether you’re building standalone executables or web apps, there are unique attributes to building desktop-class experiences. Desktop windows are usually in landscape mode and resizable; input usually comes from a physical keyboard and mouse rather than an on-screen keyboard and touch; and controls are optimized for a different screen density. + +At the framework level, we’ve made various changes to Flutter to support desktop-class experiences. + +* When you create a new project in recent builds, you’ll see that the default template now includes a reference to a [`visualDensity`](https://api.flutter.dev/flutter/material/ThemeData/visualDensity.html) property, which [allows controls to adapt their density](https://github.com/flutter/flutter/issues/43350) based on the platform they are targeting, with more compact spacing on desktop platforms. An example of how this is used is [`TextField`](https://api.flutter.dev/flutter/material/TextField-class.html), which now offers [compact, comfortable, and standard spacing](https://github.com/flutter/flutter/pull/51438) depending on the specified density. + +* We’ve added much better support for mouse and keyboard input; this includes [raw key codes on Windows](https://github.com/flutter/flutter/issues/52264), right-click mouse button, [cursor changes](https://github.com/google/flutter-desktop-embedding/issues/376) and scroll wheel support. + +* You can now query for the specific platform (through the `Platform` class) and Windows, macOS, and Linux all provide the appropriate results. + +* In the most recent release, we’ve added [a `NavigationRail` widget](https://api.flutter.dev/flutter/material/NavigationRail-class.html) that is specifically designed for desktop-class experiences on desktop and tablet. + +## FFI + +The Dart team has been hard at work polishing [Foreign Function Interface (FFI)](https://dart.dev/guides/libraries/c-interop), which is a great velocity booster for platform integration. For C-based APIs, the `dart:ffi` library provides a direct mechanism for binding to native code; the Dart runtime provides the ability to allocate memory on the heap that is backed by a Dart object and make calls to dynamically linked libraries. + +For a simple example from Windows, here’s a fragment of code to call the traditional Win32 `MessageBox()` API entirely with Dart code: + +```dart +typedef MessageBoxNative = Int32 Function( + IntPtr hWnd, Pointer<Utf16> lpText, Pointer<Utf16> lpCaption, Int32 uType); +typedef MessageBoxDart = int Function( + int hWnd, Pointer<Utf16> lpText, Pointer<Utf16> lpCaption, int uType); + +final user32 = DynamicLibrary.open('user32.dll'); +final win32MessageBox = + user32.lookupFunction<MessageBoxNative, MessageBoxDart>('MessageBoxW'); + +void showMessageBox(String message, String caption) => win32MessageBox( + 0, // No owner window + Utf16.toUtf16(message), // Message + Utf16.toUtf16(caption), // Window title + 0 // OK button only + ); + +… + +showMessageBox('Test Message', 'Window Caption'); // call just like any other Dart function +``` + +In this code, we provide `typedef`s that represent the method signature both in its native and Dart-based representations. With these defined, we can load the Windows dynamic link library (DLL) that provides the implementation of the function via the `lookupFunction()` method, which maps the Dart function signature onto the underlying native entrypoint. Lastly, we optionally add a simple idiomatic wrapper to make it easily accessible from other Dart code, resulting in something like this: + +<DashImage figure src="images/0rCBxqUkuMqFvPvq4.webp" alt="A simple Windows example app that uses the Win32 `MessageBox` API" caption="A simple Windows example app that uses the Win32 `MessageBox` API" /> + + +You don’t have to do this work yourself, of course: chances are that [someone has already paved the way for the API that you want to use](https://pub.dev/packages/win32). You can [read more about using FFI from Flutter in our docs](https://flutter.dev/docs/development/platform-integration/c-interop). + +## Updating the plugin model + +By design, Flutter itself has a small core. Rather than adding heft to the base framework, plugins and packages (whether directly from the Flutter team or the broader ecosystem of contributors), provide integration with the underlying operating systems. + +However, with Flutter increasingly supporting mobile, web, and desktop, developing a plugin for every supported platform becomes ever more challenging. It’s more likely that a plugin will require contributions from different authors with expertise in each platform. + +A useful technique is to define a common interface as part of the core plugin that each platform can implement independently. So, as described in [a recent post about modern plug-in development](https://medium.com/flutter/modern-flutter-plugin-development-4c3ee015cf5a#1139), we’ve recently adapted the schema for plug-ins to enable platform development to be more easily federated across multiple authors. As part of this work, it’s now possible to [declare explicitly which platforms are supported by a plugin](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms). + +We’ve started to build out some of the core plugins using this model, and you’ll find some early examples of the federated model in the [flutter/plugins](https://github.com/flutter/plugins/tree/master/packages/path_provider) repo. + +Note that the Windows and Linux plugin APIs are still in flux, so while we encourage exploration, we aren’t ready for general-purpose production support at this time. We’re also working on adding Desktop platform tags on pub.dev. + +## Running on Windows: Win32 and UWP + +One interesting aspect of the work that we’ve been doing on Windows is experimenting with various architectural approaches. On any platform, Flutter is embedded into a small host container app (an “embedder”), using a similar approach to game engines like Unity. This platform-specific embedder provides an entrypoint, coordinates with the underlying operating system for access to services like rendering surfaces, accessibility and input, and manages the message event loop. + +<DashImage figure src="images/1tCqp0X4-8_yDjhbtVbqx-w.webp" /> + + +Windows offers two distinct approaches for creating this embedder. First, the mature Win32 programming model can be used to create the entrypoint for Flutter content; this offers maximum backwards compatibility to platforms such as Windows 7 and builds a standard EXE file that many developers will expect. Conversely, the modern UWP app model is the recommended approach for Windows 10 and offers intriguing opportunities for expanding Flutter support to devices such as Xbox or the upcoming [Windows 10X](https://docs.microsoft.com/en-us/dual-screen/windows/get-dev-tools) operating system. + +We’ve been working informally with various contributors to explore different solutions here, and would gladly support a close collaboration with Microsoft to complete a high-quality solution. With the Surface family of devices extended to include Android and Windows, we think Flutter offers Microsoft a compelling platform for building beautiful native experiences that span their entire portfolio. + +## Having fun with desktop + +This work remains in technical preview, and the APIs and tooling are not yet stable. We’re still tracking plenty of work that we want to complete before we promote desktop support to stable, including improved accessibility and localization support. + +If you want to try it out, you’ll need to be on a development channel. Windows and Linux are only available on the `master` branch, which is where active Flutter development takes place. macOS is available on the `dev` branch, which is slightly more stable but not recommended for production use. You can switch channels with `flutter channel master` or `flutter channel dev`, and then use one of these commands to enable support for the platform you’re using: + +``` +C:\flutter> flutter config --enable-windows-desktop + +$ flutter config --enable-macos-desktop + +$ flutter config --enable-linux-desktop +``` + + +We’re already seeing some adventurous developers start to take advantage of Flutter on the desktop to create apps. One of the first Flutter macOS desktop apps that we’ve seen is [Sharezone](https://sharezone.net/), a student planner aimed at the German educational market, which started as a mobile app but has recently added web and desktop versions. + +<DashImage figure src="images/0iBeoGqAPyC66-BBe.webp" alt="Sharezone Schulplan — an app for students, educators and parents to track homework, study groups and timetables." caption="Sharezone Schulplan — an app for students, educators and parents to track homework, study groups and timetables." /> + + +We’ll be sharing more about desktop support in the coming weeks; in the meantime, we’d love to hear more about your experiences. If you’re a plugin author, we’d encourage you to start evaluating the work to support these upcoming desktop platforms; if you’ve published an app, why not try running it as a desktop app and let us know how you get on? \ No newline at end of file diff --git a/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/01g6d0g2ruUw0ZNBb.webp b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/01g6d0g2ruUw0ZNBb.webp new file mode 100644 index 0000000000..ce1bba9378 Binary files /dev/null and b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/01g6d0g2ruUw0ZNBb.webp differ diff --git a/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/08lW_z0nxESDIBO9R.gif b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/08lW_z0nxESDIBO9R.gif new file mode 100644 index 0000000000..abb91bbe7c Binary files /dev/null and b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/08lW_z0nxESDIBO9R.gif differ diff --git a/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0TRpN2WHDqNJtk7vI.gif b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0TRpN2WHDqNJtk7vI.gif new file mode 100644 index 0000000000..9d1fbfe6b3 Binary files /dev/null and b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0TRpN2WHDqNJtk7vI.gif differ diff --git a/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0UySYHB5By2kX9P_6.webp b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0UySYHB5By2kX9P_6.webp new file mode 100644 index 0000000000..b6f4429192 Binary files /dev/null and b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0UySYHB5By2kX9P_6.webp differ diff --git a/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0VdFcZ_eCsnJnJXb-.webp b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0VdFcZ_eCsnJnJXb-.webp new file mode 100644 index 0000000000..da9f3061b9 Binary files /dev/null and b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0VdFcZ_eCsnJnJXb-.webp differ diff --git a/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0_8Ynon8NQu4zB3B7.webp b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0_8Ynon8NQu4zB3B7.webp new file mode 100644 index 0000000000..f5b4c419a9 Binary files /dev/null and b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0_8Ynon8NQu4zB3B7.webp differ diff --git a/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0eouqer3J3CQE-fO4.webp b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0eouqer3J3CQE-fO4.webp new file mode 100644 index 0000000000..d11b53566c Binary files /dev/null and b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0eouqer3J3CQE-fO4.webp differ diff --git a/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0k-sSbKs-rCvbxB1a.webp b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0k-sSbKs-rCvbxB1a.webp new file mode 100644 index 0000000000..a7f40ecec5 Binary files /dev/null and b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0k-sSbKs-rCvbxB1a.webp differ diff --git a/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0wGVszwaj9kdJlOsS.webp b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0wGVszwaj9kdJlOsS.webp new file mode 100644 index 0000000000..cb5086ae28 Binary files /dev/null and b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/images/0wGVszwaj9kdJlOsS.webp differ diff --git a/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/index.md b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/index.md new file mode 100644 index 0000000000..6e932092d7 --- /dev/null +++ b/sites/www/content/blog/flutter-animation-basics-with-implicit-animations/index.md @@ -0,0 +1,182 @@ +--- +title: "Flutter animation basics with implicit animations" +description: "In this series of posts, we’re going to introduce you to animations in Flutter, and show you how to build both simple and complex…" +publishDate: 2019-12-04 +author: theaflowers +image: images/08lW_z0nxESDIBO9R.gif +layout: blog +--- + +In this series of posts, we’re going to introduce you to animations in Flutter, and show you how to build both simple and complex animations for your Flutter app. + +*This post is also available as a [video](https://www.youtube.com/watch?v=IVTjpW3W33s), if you prefer.* + +In this first post, we’re going to focus on the most straightforward way to add animations to your app. You don’t have to be an expert on animations or animation terminology to add animations to your app. Along the way, we’ll introduce some widgets and terminology that will help you get started with animations right away and give you some background for the rest of the posts in the series. + +## Implicitly animated widgets + +Flutter includes a series of widgets that are animated versions of existing widgets that you probably already used in your app, such as the [AnimatedContainer](https://api.flutter.dev/flutter/widgets/AnimatedContainer-class.html) version of the [Container](https://api.flutter.dev/flutter/widgets/Container-class.html) widget and the [AnimatedPositioned](https://api.flutter.dev/flutter/widgets/AnimatedPositioned-class.html) version of the [Positioned](https://api.flutter.dev/flutter/widgets/Positioned-class.html) widget. + +These widgets automatically animate changes to their properties. When you rebuild the widget with new property values, such as with a [StatefulWidget](https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html)’s `setState`, the widget handles driving the animation from the previous value to the new value. + +<DashImage figure src="images/08lW_z0nxESDIBO9R.gif" alt="A screen recording where the code for an app is modified and the application animates the size of a star widget." caption="A screen recording where the code for an app is modified and the application animates the size of a star widget." /> + + +These widgets are called [Implicitly Animated Widgets](https://api.flutter.dev/flutter/widgets/ImplicitlyAnimatedWidget-class.html). They are typically the first thing you reach for when you need to add animations to your app. They provide a way to add animations without adding additional complexity. + +## The AnimatedContainer widget + +Let’s look a little closer at how you can use one of these implicitly animated widgets to add some animation to your app. + +In this app, there is a container and a button. When the button is pressed, `setState` is called, and the container is rebuilt with a new value for `width`. Notice that the container changes its width immediately, without any animation. + +<DashImage figure src="images/0TRpN2WHDqNJtk7vI.gif" alt="The star gets bigger instantly, with no animation between its states." caption="The star gets bigger instantly, with no animation between its states." /> + + +``` +[@override](http://twitter.com/override) +Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + Container( + width: _bigger ? 100 : 500, + child: Image.asset('assets/star.png'), + ), + RaisedButton( + onPressed: () => setState(() { + _bigger = !_bigger; + }), + child: Icon(Icons.star), + ), + ], + ); +} +``` + + +We can add some animation to this app by switching the Container widget for an AnimatedContainer widget and specifying an animation duration. + +``` +AnimatedContainer( + width: _bigger ? 100 : 500, + child: Image.asset('assets/star.png'), + duration: Duration(seconds: 1), +), +``` + + +Now, when the button is pressed, the container gradually animates from the previous width value to the new value. + +<DashImage figure src="images/0VdFcZ_eCsnJnJXb-.webp" alt="The star now animates between its states" caption="The star now animates between its states" /> + + +The process of animating through the values between the old and new value is called [interpolation](https://en.wikipedia.org/wiki/Interpolation). The AnimatedContainer handles interpolating its properties between the old and the new values whenever they change. + +This applies to all of the AnimatedContainer’s properties, including, for example, the [decoration](https://api.flutter.dev/flutter/widgets/AnimatedContainer/decoration.html). We can modify the gradient within a decoration and the AnimatedContainer handles interpolating between the old and new gradient: + +``` +AnimatedContainer( + decoration: BoxDecoration( + gradient: RadialGradient( + colors: [Colors.purple, Colors.transparent], + stops: [ _bigger ? 0.2 : 0.5, 1.0]) + ), +), +``` + + +<DashImage figure src="images/0k-sSbKs-rCvbxB1a.webp" alt="The star now has an animated gradient" caption="The star now has an animated gradient" /> + + +## Controlling the animation with duration and curves + +Implicitly animated widgets like AnimatedContainer have two properties that you can use to control the animation’s behavior. You can control how long it takes to interpolate to the new value by setting the [duration](https://api.flutter.dev/flutter/widgets/ImplicitlyAnimatedWidget/duration.html) property. + +``` +AnimatedContainer( + width: _bigger ? 100 : 500, + child: Image.asset('assets/star.png'), + duration: Duration(seconds: 5), +), +``` + + +<DashImage figure src="images/0wGVszwaj9kdJlOsS.webp" /> + + +<DashImage figure src="images/01g6d0g2ruUw0ZNBb.webp" alt="Faster on the left, slower on the right" caption="Faster on the left, slower on the right" /> + + +In this example, we made the animation take a much longer amount of time. + +You can also control the way the widget interpolates from the old to the new value by using a [Curve](https://api.flutter.dev/flutter/animation/Curve-class.html). Curves control the rate of change over time and can help your animations feel more realistic. In this example, we changed the curve from the default linear curve to a more exaggerated quintic curve: + +``` +AnimatedContainer( + width: _bigger ? 100 : 500, + child: Image.asset('assets/star.png'), + duration: Duration(seconds: 1), + curve: Curves.easeInOutQuint, +), +``` + + +<DashImage figure src="images/0UySYHB5By2kX9P_6.webp" /> + + +<DashImage figure src="images/0_8Ynon8NQu4zB3B7.webp" alt="Linear on the left, quintic on the right" caption="Linear on the left, quintic on the right" /> + + +There are many different [built-in Curves](https://api.flutter.dev/flutter/animation/Curves-class.html) available to give your animations a bit of character, and you can also define your own custom curves. Curves can even be discontinuous, like the [SawTooth](https://api.flutter.dev/flutter/animation/SawTooth-class.html) curve. + +Here’s an example of a custom curve called SineCurve that uses the sine function to make a curve that bounces: + +``` +class SineCurve extends Curve { + final double count; + + SineCurve({this.count = 1}); + + [@override](http://twitter.com/override) + double transformInternal(double t) { + return sin(count * 2 * pi * t) * 0.5 + 0.5; + } +} +``` + + +Here, SineCurve makes the star bounce up and down: + +<DashImage figure src="images/0eouqer3J3CQE-fO4.webp" alt="Now that is a happy star" caption="Now that is a happy star" /> + + +## To recap + +Flutter provides implicitly animated widgets that are animated versions of common widgets. You can control the way these widgets animate using durations and curves. + +AnimatedContainer is one notably powerful implicitly animated widget because it has many properties that affect its appearance, and all of them are automatically interpolated. + +All of the other implicitly animated widgets are also powerful, easy-to-use options for adding animations without adding a lot of complexity. + +Also, you don’t necessarily need to place these widgets into a StatefulWidget and use setState, you can use [StreamBuilder](https://api.flutter.dev/flutter/widgets/StreamBuilder-class.html) and [FutureBuilder](https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html) to trigger animations like in [this example](https://gist.github.com/theacodes/98255cd57845731449582e12b132ed6d). + +## Digging deeper into animations + +Implicitly animated widgets are your first choice for adding animations, however, this isn’t all that Flutter’s animation system has to offer. In the rest of this series, we explore the lower layers of the Flutter animation system, and show you how you can build advanced animations by using the animation system directly. + +For everything else, visit [flutter.dev](https://flutter.dev). + +Articles in this series: + +* [How to Choose Which Flutter Animation Widget is Right for You?](https://medium.com/p/79ecfb7e72b5/edit) + +* [Flutter animation basics with implicit animations](https://medium.com/flutter/flutter-animation-basics-with-implicit-animations-95db481c5916) (this article) + +* [Custom Implicit Animations in Flutter…with TweenAnimationBuilder](https://medium.com/flutter/custom-implicit-animations-in-flutter-with-tweenanimationbuilder-c76540b47185) + +* [Directional animations with built-in explicit animations](https://medium.com/flutter/directional-animations-with-built-in-explicit-animations-3e7c5e6fbbd7) + +* [When should I useAnimatedBuilder or AnimatedWidget?](https://medium.com/flutter/when-should-i-useanimatedbuilder-or-animatedwidget-57ecae0959e8) + +* [Animation deep dive](https://medium.com/flutter/animation-deep-dive-39d3ffea111f) \ No newline at end of file diff --git a/sites/www/content/blog/flutter-codepen-challenge/images/0lBCX1cQ8WF56oRAq.webp b/sites/www/content/blog/flutter-codepen-challenge/images/0lBCX1cQ8WF56oRAq.webp new file mode 100644 index 0000000000..f1b6eddf9f Binary files /dev/null and b/sites/www/content/blog/flutter-codepen-challenge/images/0lBCX1cQ8WF56oRAq.webp differ diff --git a/sites/www/content/blog/flutter-codepen-challenge/index.md b/sites/www/content/blog/flutter-codepen-challenge/index.md new file mode 100644 index 0000000000..5ebe9a5dcf --- /dev/null +++ b/sites/www/content/blog/flutter-codepen-challenge/index.md @@ -0,0 +1,41 @@ +--- +title: "Flutter CodePen challenge" +description: "Show your Flutter skills by building beautiful UIs on CodePen" +publishDate: 2020-07-01 +author: anjannarain +image: images/0lBCX1cQ8WF56oRAq.webp +category: events +layout: blog +--- + +<DashImage figure src="images/0lBCX1cQ8WF56oRAq.webp" /> + + +From the outset, one of our goals with Flutter was to enable developers to create beautiful user experiences. And every day the worldwide community amazes us with [countless apps and experiments](https://twitter.com/i/events/1275593667017834502?s=20) that showcase Flutter’s creative potential. Today, we’re pleased to partner with CodePen on a fun series of new challenges to let you show off your Flutter skills. + +CodePen is one of the top destinations on the internet for front-end developers to experiment, share, and iterate. Ever since we announced our [partnership with CodePen](https://medium.com/flutter/announcing-codepen-support-for-flutter-bb346406fe50) in April, we’ve been amazed by the Flutter Pens you’ve created. And so we want to give you the opportunity to show off your amazing Flutter designs to CodePen’s community of over a million designers and coders. + +## Challenge details + +**What is it?** CodePen Challenges are fun opportunities to level up your skills by building things. Previous challenges have covered web development themes like [JavaScript](https://codepen.io/challenges/2020/march/), [Images](https://codepen.io/challenges/2020/april/), and [Color Palettes](https://codepen.io/challenges/2019/january/). The Flutter CodePen challenge is a month-long challenge in July to build user experiences with [CodePen’s new Flutter editor](https://codepen.io/pen/editor/flutter). + +**How it works:** + +1. The Flutter CodePen challenge consists of 4 weekly challenge prompts, each on a different Flutter topic. Each prompt will be released at the beginning of the week, and the prompts will progress from basic to more advanced, building on each other. + +1. To help you, we’ll share some ideas on what to build, recommend resources, and share example pens. + +1. Once you complete your Pen, we encourage you to share it with the CodePen community using the tag **FlutterPen** and the broader developer community on Twitter and LinkedIn with **#FlutterPen**. The CodePen community will select the best Pens, which will be showcased on CodePen’s homepage. + +**Date:** The first weekly challenge will be released Monday, July 6th, 2020. + +**Sneak peek:** For the first week, we’ll start with one of the most foundational building blocks in Flutter: a [Stack widget](https://www.youtube.com/watch?v=liEGSeD3Zt8). Stack lets you layer widgets on top of each other in paint order. You can use it in simple scenarios such as [overlaying texts on top of gradients](https://api.flutter.dev/flutter/widgets/Stack-class.html#widgets.Stack.2) or building some really cool custom designs. + +Here’s an embedded CodePen that uses the Stack widget to create a three-panel view. Click **Run Pen** to see the app in action. Then click **Flutter** to see the code that implements the app. + +<iframe src="https://codepen.io/mkiisoft/embed/XWmGdxL" width="800" height="600" frameborder="0" allowfullscreen></iframe> + + +## Take the challenge! + +Get creative and check out the [challenge page](https://codepen.io/challenges/), starting July 6th! In the meantime, why not use the **#FlutterPen** hashtag to show off the Flutter Pens you’ve already created? We’re eager to see what you’ve built so far! \ No newline at end of file diff --git a/sites/www/content/blog/flutter-create-is-live-with-over-10-000-in-prizes/images/15XImuQBc3XbmTW9RZTbUEg.webp b/sites/www/content/blog/flutter-create-is-live-with-over-10-000-in-prizes/images/15XImuQBc3XbmTW9RZTbUEg.webp new file mode 100644 index 0000000000..1d96bca897 Binary files /dev/null and b/sites/www/content/blog/flutter-create-is-live-with-over-10-000-in-prizes/images/15XImuQBc3XbmTW9RZTbUEg.webp differ diff --git a/sites/www/content/blog/flutter-create-is-live-with-over-10-000-in-prizes/images/1AyxOZQPLHstjafeVvwHbUg.webp b/sites/www/content/blog/flutter-create-is-live-with-over-10-000-in-prizes/images/1AyxOZQPLHstjafeVvwHbUg.webp new file mode 100644 index 0000000000..bd074c59df Binary files /dev/null and b/sites/www/content/blog/flutter-create-is-live-with-over-10-000-in-prizes/images/1AyxOZQPLHstjafeVvwHbUg.webp differ diff --git a/sites/www/content/blog/flutter-create-is-live-with-over-10-000-in-prizes/index.md b/sites/www/content/blog/flutter-create-is-live-with-over-10-000-in-prizes/index.md new file mode 100644 index 0000000000..107742c80c --- /dev/null +++ b/sites/www/content/blog/flutter-create-is-live-with-over-10-000-in-prizes/index.md @@ -0,0 +1,40 @@ +--- +title: "Flutter Create is live with over $10,000 in prizes" +description: "Tl;DR: Flutter Create is a contest that challenges you to build something interesting, inspiring, and beautiful with Flutter using 5KB or…" +publishDate: 2019-02-21 +author: aguinis +image: images/1AyxOZQPLHstjafeVvwHbUg.webp +category: events +layout: blog +--- + +**Tl;DR: Flutter Create is a contest that challenges you to build something interesting, inspiring, and beautiful with Flutter using 5KB or less of Dart code. Whether you’re an expert or a beginner with Flutter, we invite you to join us and see what you can create. We also have over $10,000 in prizes for a few winners! Visit [flutter.dev/create](http://flutter.dev/create) to enter.** + +<YoutubeEmbed id="BKl6gUvvZGs" title="Introducing Flutter Create" fullwidth="true"/> + + +We are introducing Flutter Create, an initiative where new and experienced developers are asked to submit a Flutter-related project in less than 5KB of Dart code. + +5KB is less than half a second of a typical MP3 file. What can you do with just 5KB? It turns out — quite a lot. Flutter includes widgets and libraries that make developers productive with only a little code. + +So we thought we’d see what you can do. Amaze us with what you can build with Dart in just 5 kilobytes, and you might win one of the following prizes: + +<DashImage figure src="images/15XImuQBc3XbmTW9RZTbUEg.webp" /> + + +Winners will be announced at Google I/O 2019. + +*[flutter.dev/create](http://flutter.dev/create) is your source for all challenge details and submissions.* + +<DashImage figure src="images/1AyxOZQPLHstjafeVvwHbUg.webp" /> + + +**High Level Details** + +***Date:** All projects must be submitted by April 7 11:59pm PDT (GMT -7).* + +***How to Submit:** Entries will be collected on the form linked at [flutter.dev/create](http://flutter.dev/create).* + +***Winners & Prizes:** Submissions will be rated by Flutter experts against the following rubric: visual beauty, code quality, novelty of idea, and overall execution. Prizes include a fully loaded iMac Pro, Google Home Max, and Google Home Mini.* + +Good luck and have fun! \ No newline at end of file diff --git a/sites/www/content/blog/flutter-culture-and-how-to-preserve-it/images/1-P8cY8Ia17Xq1ufedJKyiQ.webp b/sites/www/content/blog/flutter-culture-and-how-to-preserve-it/images/1-P8cY8Ia17Xq1ufedJKyiQ.webp new file mode 100644 index 0000000000..32546a3073 Binary files /dev/null and b/sites/www/content/blog/flutter-culture-and-how-to-preserve-it/images/1-P8cY8Ia17Xq1ufedJKyiQ.webp differ diff --git a/sites/www/content/blog/flutter-culture-and-how-to-preserve-it/images/1I4r3rDh8039olbgfWiztkw.webp b/sites/www/content/blog/flutter-culture-and-how-to-preserve-it/images/1I4r3rDh8039olbgfWiztkw.webp new file mode 100644 index 0000000000..c437c53566 Binary files /dev/null and b/sites/www/content/blog/flutter-culture-and-how-to-preserve-it/images/1I4r3rDh8039olbgfWiztkw.webp differ diff --git a/sites/www/content/blog/flutter-culture-and-how-to-preserve-it/images/1cuQ-MCimzPmxiJjL-UV3KQ.jpeg b/sites/www/content/blog/flutter-culture-and-how-to-preserve-it/images/1cuQ-MCimzPmxiJjL-UV3KQ.jpeg new file mode 100644 index 0000000000..c4f2da740f Binary files /dev/null and b/sites/www/content/blog/flutter-culture-and-how-to-preserve-it/images/1cuQ-MCimzPmxiJjL-UV3KQ.jpeg differ diff --git a/sites/www/content/blog/flutter-culture-and-how-to-preserve-it/images/1pI9tDcnxm87Vq1hX1Yzwpg.jpeg b/sites/www/content/blog/flutter-culture-and-how-to-preserve-it/images/1pI9tDcnxm87Vq1hX1Yzwpg.jpeg new file mode 100644 index 0000000000..ef8e8a3fe5 Binary files /dev/null and b/sites/www/content/blog/flutter-culture-and-how-to-preserve-it/images/1pI9tDcnxm87Vq1hX1Yzwpg.jpeg differ diff --git a/sites/www/content/blog/flutter-culture-and-how-to-preserve-it/images/1wgRQ5H3y-fauvzGLxM3HDw.jpeg b/sites/www/content/blog/flutter-culture-and-how-to-preserve-it/images/1wgRQ5H3y-fauvzGLxM3HDw.jpeg new file mode 100644 index 0000000000..c47dac8319 Binary files /dev/null and b/sites/www/content/blog/flutter-culture-and-how-to-preserve-it/images/1wgRQ5H3y-fauvzGLxM3HDw.jpeg differ diff --git a/sites/www/content/blog/flutter-culture-and-how-to-preserve-it/index.md b/sites/www/content/blog/flutter-culture-and-how-to-preserve-it/index.md new file mode 100644 index 0000000000..9dbf06a908 --- /dev/null +++ b/sites/www/content/blog/flutter-culture-and-how-to-preserve-it/index.md @@ -0,0 +1,90 @@ +--- +title: "Flutter culture and how to preserve it" +description: "A big part of what makes Flutter so delightful and productive is its community. We really want this to continue." +publishDate: 2019-06-07 +author: filiph +image: images/1pI9tDcnxm87Vq1hX1Yzwpg.jpeg +category: spotlight +layout: blog +--- + +*[[Emily Fortuna](https://medium.com/@emily.fortuna) and [Filip Hracek](https://medium.com/@filiph) wrote this article together.]* + +A big part of what makes Flutter so delightful and productive is its community. We really want this to continue, and so we thought we’d share some of our observations about the current culture, and some ideas on how to preserve it. + +<DashImage figure src="images/1cuQ-MCimzPmxiJjL-UV3KQ.jpeg" /> + + +## Being open + +There are three senses of the word *open* we’re thinking of here: *open* as in *open minded*, and open as in *accessible*, and *open* as in Open Source. While the Open Source angle is great (and makes our package system a veritable rainforest biome of abundance), culture-wise we’re thinking primarily about the first two. It has been wonderful to see the Flutter community welcome newcomers and help them learn the ropes, answering questions with patience and thoroughness. This helps our community grow and feel inviting to everyone, regardless of your background. We love seeing how Flutter developers share code snippets and have a willingness to dive into debugging others’ problems. + +Of course, open-mindedness is applicable to much more than just newcomers. There often can be more than one way to implement an idea, and we appreciate tremendously that the Flutter community *gently* corrects issues as they arise or allows different ways of implementing algorithms as the case may be. That, again, is powerful. + +*Some* amount of standardization is good. A small team should probably have a standardized code style. But it’s really easy for tech communities to get a little overzealous about the “right” way to do things. Different teams will have different code styles, and that’s okay. Different apps will use different state management approaches, and that’s also okay. Some people will use tabs instead of spaces, and that’s (*visibly struggles for 15 minutes*) okay. + +Again, it’s easy to overlook how *natural* it is for tech communities to become rigid about some ideas, to gain immutable institutional knowledge and defend it with fanatical zeal. + +<DashImage figure src="images/1pI9tDcnxm87Vq1hX1Yzwpg.jpeg" /> + + +Yet in the Flutter community, we haven’t seen any of that. + +Approaching questions with a beginner’s mindset, that is with openness, gentleness, curiosity, thoroughness, and a lack of preconceptions serves everyone. Not only does it encourage newcomers, it furthers the understanding of Flutter veterans, who may have only had a partial understanding of a subject area. And, simply, it makes other folks more receptive to your ideas. We are thrilled to see these positive qualities often on Stack Overflow and various Slack channels. See also, [vulnerability as a superpower](https://www.ted.com/talks/brene_brown_on_vulnerability?language=en). + +## Being modest + +We see Flutter developers being helpful to newcomers without looking down at them. This is extremely powerful. + +It’s very natural to want to look perfect in front of others. But if taken too far it leads to a culture of masks and unrealistic expectations. Things like this, when left unchecked, can spiral out of control via feedback loops. It can start with little snide remarks and next thing you know, you have widespread imposter syndrome, insecurity, anxiety, and cruelty. + +We hope we’ve shown through the various Flutter Boring Show episodes that we make mistakes. (Because we certainly do make mistakes!) The Boring Show’s concept is about showing the development process with all the errors, blind alleys, and synapse lapses along the way, without editing. + +This is one of the core qualities our team looks for in prospective Flutter Google Developer Experts (GDEs). We expect Flutter GDEs, the people who are some of the most knowledgeable about Flutter on the planet, to demonstrate humility and empathy. We don’t want Flutter to be represented by a group of smart but patronizing people. Thankfully, we never had any trouble with any of the prospective GDEs. The message of humbleness always resonated without us even having to explain what we mean. That’s a testament to our community. + +Fortunately we see this in the Flutter engineering team as well. To call just one example, Ian — one of the founders of Flutter and former editor of the HTML spec, among other things — is as humble an engineer as anyone we know. You can see him here being uncertain about the workings of Slivers. That makes us respect him more, not less. + +<YoutubeEmbed id="Mz3kHQxBjGg" title="Slivers Explained - Making Dynamic Layouts (The Boring Flutter Development Show, Ep. 12)" fullwidth="true"/> + + +## Being respectful to other technologies + +It’s easy to get so attached to a technology to the point that all others look like a bad idea. Especially when they’re competing technologies. + +Flutter doesn’t need hype or zealotry. There are very good reasons to use Flutter such as wanting to take advantage of Dart to write a reactive UI quickly, having a fast responsive app that works on iOS and Android (and the web and desktop), and having a rich package ecosystem easily within reach. + +But some people also have use cases that are not a good fit for Flutter, and one of the best things you can do as a Flutter enthusiast is to persuade these people to ***not*** use Flutter. You will save them a lot of pain and disappointment. And that person will respect you *more* for knowing the technological landscape extensively enough to provide an even-handed recommendation. + +Unfortunately, Filip [has](https://twitter.com/filiphracek/status/997324648164478976) already seen some derision from the Flutter community towards other technologies. But it hasn’t been widespread, and others in the community have stood against it. That makes us optimistic. + +<DashImage figure src="images/1wgRQ5H3y-fauvzGLxM3HDw.jpeg" /> + + +## Why preserve this? + +This community culture is an enormous strength of Flutter. Let’s be real: the developer experience of any technology is a sum of both the technology itself and the community around it. If a community consists of just fanatics, jerks, or hypers, no amount of technical superiority will make us join it. + +A healthy community is one that grows. There can be hidden costs behind a community growing while simultaneously becoming more insular. Yes, *some* new folks are joining, but can you imagine how many *additional* people might join if that community if it were more welcoming? We want to preserve these qualities that brought *us* to this community and continue to open it up for those that join after us. + +## How to preserve this? + +There isn’t a top-down way to preserve these values. Flutter is an open community. Its values will be preserved through the day-to-day decisions of its members. Small things like how you welcome new members, how you share your knowledge, how honest you are about your latest mistake, how nicely you talk about a competing framework. All those things ***are*** the Flutter community. + +<DashImage figure src="images/1I4r3rDh8039olbgfWiztkw.webp" /> + + +By focusing on openness, modesty, and respect to others in the ecosystem we will continue to foster a great developer experience for everyone. We hope that by identifying and naming what we see in the community, this article will help preserve it. After all, it’s hard to preserve something without realizing we have it. + +## What we don’t mean + +* We don’t mean we shouldn’t be showing best practices and great code. You can be excellent in your craft without looking down on others. + +* We don’t mean we shouldn’t have any standards and “primary ways to do things.” You can have standards without being dismissive about alternatives. + +* We don’t mean we shouldn’t be enthusiastic. You can be enthusiastic without hating on competing technologies. + +* We don’t mean we shouldn’t be excited about Flutter. You can be excited and still know exactly when and why Flutter is not a good tool for the job. + +As Flutter continues to grow in popularity, we are at an important point in our community’s history, and *you* are a crucial part of it! Let’s grow in the right way to continue to foster a great experience for everyone. When interacting with other folks on the internet remember to let the qualities of **openness**, **modesty**, and **respect to other ecosystems** guide your responses. Flutter has a [Code of Conduct](https://github.com/flutter/flutter/blob/master/CODE_OF_CONDUCT.md) and, as we mention there, should you experience anything that makes you feel unwelcome in Flutter’s community, please contact someone on the team such as one of us. And if you’re new here, welcome! We’re so excited you can join us. + +<DashImage figure src="images/1-P8cY8Ia17Xq1ufedJKyiQ.webp" /> diff --git a/sites/www/content/blog/flutter-darts-2026-roadmap/images/1Nx4rPo-suELodUsEQqLFdQ.webp b/sites/www/content/blog/flutter-darts-2026-roadmap/images/1Nx4rPo-suELodUsEQqLFdQ.webp new file mode 100644 index 0000000000..ca7cdc3c0b Binary files /dev/null and b/sites/www/content/blog/flutter-darts-2026-roadmap/images/1Nx4rPo-suELodUsEQqLFdQ.webp differ diff --git a/sites/www/content/blog/flutter-darts-2026-roadmap/index.md b/sites/www/content/blog/flutter-darts-2026-roadmap/index.md new file mode 100644 index 0000000000..35bc0d5481 --- /dev/null +++ b/sites/www/content/blog/flutter-darts-2026-roadmap/index.md @@ -0,0 +1,61 @@ +--- +title: "Flutter & Dart’s 2026 roadmap" +description: "Transparency is a core goal of the Flutter open source project, and today we’re happy to share our roadmap for the next generation of apps…" +publishDate: 2026-02-24 +author: twerske +image: images/1Nx4rPo-suELodUsEQqLFdQ.webp +layout: blog +--- + +<DashImage figure src="images/1Nx4rPo-suELodUsEQqLFdQ.webp" /> + + +## Flutter & Dart’s 2026 roadmap + +Transparency is a core goal of the Flutter open source project, and today we’re happy to share our roadmap for the next generation of apps. Our mission remains largely unchanged, we’re still working toward our long-term goal of creating the most popular, fastest growing, and highest-productivity multi-platform UI framework. You can check out the [**freshly updated roadmap on GitHub**](https://github.com/flutter/flutter/blob/master/docs/roadmap/Roadmap.md) for all the details, and in this post we will cover the themes driving our mission. + +As you dive into our plans for the year, keep in mind that this roadmap is — as it’s always been — our **aspirational** **strategy** for what’s ahead. As with any other roadmap, plans tend to shift and adapt throughout the year, so don’t be surprised if some changes happen along the way. While it primarily reflects the work our teams at Google are focusing on, the truth is that the Flutterverse is now so much bigger than any one company. + +## High-fidelity multiplatform: Impeller, Wasm, and beyond + +Our driving mission is to continue to deliver the best multiplatform stack by focusing on native-level quality and performance. In 2026, we are completing the migration to the [Impeller](https://docs.flutter.dev/perf/impeller) renderer on Android. By finally removing the legacy Skia backend on Android 10 and above, we’re ensuring smooth animations and reducing jank for every user. We continue to see Impeller as the best solution for fast startup and consistent performance. + +On the web, we intend for [WebAssembly (Wasm)](https://webassembly.org/) to become the default to deliver native-quality experiences and performance. We are also committed to deep platform integration, ensuring day-zero support for Android 17 and upcoming iOS releases, alongside multi-window support for desktop where our partners at Canonical continue to make progress. + +## GenUI, ephemeral experiences and agentic apps + +We are fundamentally changing app architecture to enable agentive UIs — interfaces that aren’t just pre-built, but adapt in real-time to user intent. This is powered by the [Flutter GenUI SDK](https://docs.flutter.dev/ai/genui) and the [A2UI protocol](https://a2ui.org/), enabling AI models to generate rich experiences dynamically. + +To support this, we are investigating evolving the Dart language by adding support for interpreted bytecode in the Dart runtime. This enables “ephemeral” code delivery, where specific portions of an app can be loaded on demand without requiring a full app store update — a critical technical step for truly agentic apps. + +## Full-stack Dart: Bring your tooling everywhere + +We are broadening our stack to support the evolution towards full-stack and agentive apps. A major focus is Dart Cloud Functions for Firebase, providing ~10ms cold starts to ensure high-performance backend logic. We are also investigating Dart support for the Google Cloud SDK to enable you to easily connect and build your backend on Google Cloud. Additionally, we are working with the Genkit team on enabling Dart support to help you build sophisticated AI features. + +## AI-reimagined developer experience + +AI coding agents are disrupting the way apps are built. We’ll continue to collaborate within Google to ensure Dart and Flutter have top-tier support in [Gemini CLI](https://docs.flutter.dev/ai/create-with-ai#gemini-cli) and Antigravity, ensuring core workflows like stateful hot reload work seamlessly with AI agents. + +We are also investing in [MCP (Model Context Protocol)](https://docs.flutter.dev/ai/mcp-server) servers for Dart tooling, enabling AI agents to perform complex refactors and choose secure, performant libraries with high accuracy by “talking” directly to the Dart analyzer. + +## Sustainable open-source & governance + +To unlock Flutter’s full potential, we are moving towards an open and sustainable operating model. This includes decoupling the Material and Cupertino design systems into standalone packages to accelerate development and allow them to evolve independently. We are also improving the extensibility of the Flutter Engine so support for new platforms can be authored “out-of-tree.” + +We are deepening our commitment to the ecosystem by formalizing how we collaborate with stakeholders. Central to this is the expansion of our Consultancy Program, Insiders and Google Developer Expert (GDE) network, Customer Advisory Board (CAB), and Partners Advisory Board (PAB), which provide direct feedback to our teams. + +## Modern syntax & compiled performance + +Dart continues to evolve as a high-performance language. In 2026, we plan to ship Primary Constructors to streamline class declarations and Augmentations to simplify code generation. We will continue to focus on improving build_runner and Dart/Wasm compilation, while refactoring the analyzer to improve performance for large-scale applications. + +## Bringing developers to Flutter and Dart + +Our recently completed new [Dart and Flutter learning pathway](https://docs.flutter.dev/learn/pathway) provides a streamlined, guided onboarding path for new builders. In 2026, we plan to continue our outreach both in-person and across digital platforms to improve the experience for developers and their AI tools. + +## See you at Google Cloud Next & I/O! + +While this roadmap is our plan for the year, you won’t have to wait long to see the next big things from the team. Mark your calendars for [Google Cloud Next 2026](https://cloud.google.com/next) in Las Vegas (April 22–24) and [Google I/O 2026](https://io.google/2026/) (May 19–20). These will be the best moments to see Flutter in action next! + +With non-Google contributors now outnumbering those of us employed by Google, there is an incredible amount of exciting work happening that isn’t even captured on this list! Accurate forecasting is always a challenge in an open source project, so please take this as a sincere **statement of our intent** and priorities rather than a fixed guarantee. We are unbelievably excited to keep building the future of apps alongside all of you and can’t wait to see what you’ll build! + +**What are you most excited for?** Let us know on socials with the hashtag **#Flutter2026**. And if you’re excited about hearing more from us, be sure to follow us on [**X**](https://x.com/flutterdev), check out the repos on [**GitHub**](https://github.com/flutter/flutter), and keep an eye on this **blog** for the latest updates. Let’s build the future of apps together! 🚀 \ No newline at end of file diff --git a/sites/www/content/blog/flutter-dont-fear-the-garbage-collector/images/1oVVba1QhXL1hUBKE9sfenw.webp b/sites/www/content/blog/flutter-dont-fear-the-garbage-collector/images/1oVVba1QhXL1hUBKE9sfenw.webp new file mode 100644 index 0000000000..c4d21fd783 Binary files /dev/null and b/sites/www/content/blog/flutter-dont-fear-the-garbage-collector/images/1oVVba1QhXL1hUBKE9sfenw.webp differ diff --git a/sites/www/content/blog/flutter-dont-fear-the-garbage-collector/images/1pNaeZ0l8oMCP-f1UUs-V1g.webp b/sites/www/content/blog/flutter-dont-fear-the-garbage-collector/images/1pNaeZ0l8oMCP-f1UUs-V1g.webp new file mode 100644 index 0000000000..fc77be879f Binary files /dev/null and b/sites/www/content/blog/flutter-dont-fear-the-garbage-collector/images/1pNaeZ0l8oMCP-f1UUs-V1g.webp differ diff --git a/sites/www/content/blog/flutter-dont-fear-the-garbage-collector/index.md b/sites/www/content/blog/flutter-dont-fear-the-garbage-collector/index.md new file mode 100644 index 0000000000..1e7462fad5 --- /dev/null +++ b/sites/www/content/blog/flutter-dont-fear-the-garbage-collector/index.md @@ -0,0 +1,69 @@ +--- +title: "Flutter: Don’t Fear the Garbage Collector" +description: "Flutter uses Dart as a development language and as a runtime. Dart’s runtime is ever-present, both in debug and release modes, but there…" +publishDate: 2019-01-04 +author: mjohnsullivan +image: images/1oVVba1QhXL1hUBKE9sfenw.webp +category: tutorial +layout: blog +--- + +Flutter uses [Dart](https://www.dartlang.org/) as a development language and as a runtime. Dart’s runtime is ever-present, both in debug and release modes, but there are big differences between the two build flavors. + +In debug mode, most of Dart’s plumbing is shipped to the device: the Dart runtime, the just-in-time compiler/interpreter (JIT for Android and interpreter for iOS), debugging and profiling services. In release mode, the JIT/interpreter and debugging services are stripped out, but the runtime remains, and this is a major contributor to the base size of a Flutter app. + +<DashImage figure src="images/1oVVba1QhXL1hUBKE9sfenw.webp" alt="*Dart components in Flutter’s debug and release modes*" caption="*Dart components in Flutter’s debug and release modes*" /> + + +Dart’s runtime includes a garbage collector, a necessary component for allocating and deallocating memory as objects are instantiated and become unreachable. + +And with Flutter, that can be *a lot* of objects. Stateless Widgets are created as they’re rendered on screen, destroyed and rebuilt when the app’s state changes or when they’re no longer visible, most with a short lifespan. For an app with a reasonably complex UI, that can run to thousands of widgets. + +So should Flutter developers fear the garbage collector? With Flutter creating and destroying objects with great frequency, should developers take steps to limit this behavior? It’s not uncommon to see new Flutter developers create references to widgets they know will not change over time, and place them in state so that they won’t be destroyed and rebuilt. + +Don’t do this. + +Fearing Dart’s garbage collector is largely unfounded, due to its generational architecture and an implementation that is optimized for the rapid creation and destruction of objects. In most scenarios, you should let Flutter’s engine create and destroy all the widgets it likes. + +## The Dart Garbage Collector + +Dart’s garbage collector is [generational](https://en.wikipedia.org/wiki/Tracing_garbage_collection#Generational_GC_(ephemeral_GC)) and consists of two phases: the young space scavenger and parallel mark sweep collectors. + +### Scheduling + +To minimize the effects of garbage collection on app and UI performance, the garbage collector provides hooks to the Flutter engine that alerts it when the engine detects that the app is idle and there’s no user interaction. This gives the garbage collector windows of opportunity to run its collection phases without impacting performance. + +The garbage collector can also run sliding compaction during those idle intervals, which minimizes memory overhead by reducing memory fragmentation. + +### Young Space Scavenger + +This phase is designed to clean up ephemeral objects that have a short lifespan, such as stateless widgets. While it is blocking, it is much faster than the second generation mark/sweep, and when combined with scheduling, virtually eliminates perceived pauses in the app when run. + +In essence, objects are allocated to a contiguous space in memory, and as objects are created, they’re allocated the next available space, until the allocated memory is filled. Dart uses bump pointer allocation to rapidly allocate in new space, making the process very fast. + +The new space (or nursery), where new objects are allocated, consists of two halves, known as semi spaces. Only one half is used at any time: one being active, the other inactive. New objects are allocated in the active half, and once that is filled, live objects are copied from active to inactive, ignoring dead objects. The inactive half then becomes active and the process repeats. + +To determine which objects are alive or dead, the collector starts with root objects, such as stack variables, and examines what they reference. It then moves the referenced objects. From there it examines what these evacuated objects point to, and moves these referenced objects. This continues until all live objects are moved. Dead objects have no references and are thus left behind; live objects will be copied over them in a future garbage collection event. + +For more information on this, check out [Cheney’s algorithm](https://en.wikipedia.org/wiki/Cheney%27s_algorithm). + +<DashImage figure src="images/1pNaeZ0l8oMCP-f1UUs-V1g.webp" alt="*Young Space Scavenger garbage collection*" caption="*Young Space Scavenger garbage collection*" /> + + +### Parallel Marking and Concurrent Sweeping + +When objects achieve a certain lifespan, they are promoted to a new memory space, managed by the second generation collector: mark-sweep. + +This garbage collection technique has two phases: the object graph is first traversed and objects that are still in use are marked. During the second phase the entire memory is scanned, and any objects not marked are recycled. All flags are then cleared. + +This form of garbage collection blocks on the marking phase; no memory mutation can occur and the UI thread is blocked. This collection is more infrequent as short-lived objects are handled by the young scavenger, but there will be times when the Dart runtime needs to pause in order to run this form of garbage collection. Given Flutter’s ability to schedule collection, the impact of this should be minimized. + +It should be noted that if an app doesn’t adhere to the *weak generational hypothesis* (which states that most objects die young), then this form of collection will occur more often. Given how Flutter’s widget implementation works, this is unlikely, but something to bear in mind. + +## Isolates + +It’s worth noting that Dart isolates have their own private heap, independent of one another. As each isolate runs in a separate thread, garbage collection events for each isolate should not impact the performance of others. Using [isolates](https://docs.flutter.io/flutter/foundation/compute.html) is a great way to avoid blocking the UI and offloading process intensive activities. + +## Wrapping Up + +And there you have it: Dart employs a powerful generational garbage collector to minimize the effects for blocking garbage collection in Flutter apps. Don’t fear the garbage collector; it’s got your app’s best interests at heart. \ No newline at end of file diff --git a/sites/www/content/blog/flutter-for-web-early-adopter-program-now-open/images/1HcggEjhP_Li6Tt7KIzB10g.webp b/sites/www/content/blog/flutter-for-web-early-adopter-program-now-open/images/1HcggEjhP_Li6Tt7KIzB10g.webp new file mode 100644 index 0000000000..3f6bba3c69 Binary files /dev/null and b/sites/www/content/blog/flutter-for-web-early-adopter-program-now-open/images/1HcggEjhP_Li6Tt7KIzB10g.webp differ diff --git a/sites/www/content/blog/flutter-for-web-early-adopter-program-now-open/index.md b/sites/www/content/blog/flutter-for-web-early-adopter-program-now-open/index.md new file mode 100644 index 0000000000..ee167821fc --- /dev/null +++ b/sites/www/content/blog/flutter-for-web-early-adopter-program-now-open/index.md @@ -0,0 +1,26 @@ +--- +title: "Flutter for web early adopter program now open" +description: "Ready for active development of production-quality web apps" +publishDate: 2019-07-25 +author: kevmoo +image: images/1HcggEjhP_Li6Tt7KIzB10g.webp +category: announcements +layout: blog +--- + +Our vision for Flutter has always been to provide a fast, productive and open toolkit for creating beautiful user experiences on any platform. We started with mobile, with a toolkit that is already in use for tens of thousands of apps, big and small. At Google I/O this year, we announced the [first preview of Flutter support for the web](https://developers.googleblog.com/2019/05/Flutter-io19.html), allowing developers to use the same skills and code to target the web browser, and we’ve seen a *lot* of interest already. Thank you to the thousands of you who are already experimenting with Flutter on the web and giving us your feedback. + +<DashImage figure src="images/1HcggEjhP_Li6Tt7KIzB10g.webp" /> + + +We’re excited to announce that we are moving into a new phase of development as we get closer to a production-quality release of our web support. Today, we are [opening an exclusive program for companies, design agencies and startups](https://forms.gle/32JCpbYefLaZE3xp8) who are ready to build in earnest on Flutter for the web. We have a limited number of slots available by application to those who are ready to engage with us directly on a first wave of production Flutter web applications. + +We are looking for candidates with a compelling scenario, a plan to release a web-based Flutter experience within the next six to twelve months, as well as willingness to be featured as a showcase in marketing and launch activities. Like any preview program, there will inevitably be rough edges along the way, but we’re ready to provide support and early access to overcome any obstacles that you experience. + +Accepted participants will receive priority support and an opportunity to highlight their projects in Google / Flutter media and events. + +## How to apply + +[Apply for consideration by completing the form](https://forms.gle/32JCpbYefLaZE3xp8). We’ll be accepting qualified applications from now through the end of August. + +More information on Flutter’s support for the web can be found at [flutter.dev/web](https://flutter.dev/web). We’re looking forward to hear from you! \ No newline at end of file diff --git a/sites/www/content/blog/flutter-hot-reload/images/1iaLz5LEBdsZJRR_W8UCQcQ.webp b/sites/www/content/blog/flutter-hot-reload/images/1iaLz5LEBdsZJRR_W8UCQcQ.webp new file mode 100644 index 0000000000..08f4ee3efc Binary files /dev/null and b/sites/www/content/blog/flutter-hot-reload/images/1iaLz5LEBdsZJRR_W8UCQcQ.webp differ diff --git a/sites/www/content/blog/flutter-hot-reload/index.md b/sites/www/content/blog/flutter-hot-reload/index.md new file mode 100644 index 0000000000..ace75453b8 --- /dev/null +++ b/sites/www/content/blog/flutter-hot-reload/index.md @@ -0,0 +1,156 @@ +--- +title: "Flutter Hot Reload" +description: "What happens during a hot reload, and how we made it faster for Flutter 2.2." +publishDate: 2021-08-19 +author: jensjohansen +image: images/1iaLz5LEBdsZJRR_W8UCQcQ.webp +category: announcements +layout: blog +--- + +Hot reload was fast in Flutter 2.0, but it’s faster in Flutter 2.2. Read on for details on hot reload in general, and how we made it faster for Flutter 2.2. + +<DashImage figure src="images/1iaLz5LEBdsZJRR_W8UCQcQ.webp" /> + + +## Introduction + +One of Flutter’s great features is *hot reload*. You press `r` on your keyboard, and moments later the impact of your changes can be seen on the device. In your terminal (or maybe at the bottom of your IDE) you can read something like `Reloaded 1 of 553 libraries in 297ms`. But what actually goes on under the hood when you initiate a hot reload, and how did the Dart and Flutter teams make it even faster? + +## Overview of a hot reload + +A hot reload in Flutter roughly consists of the following five steps: + +1. `flutter_tools` scans the needed files for changes. It looks at every needed file and checks if its last changed timestamp is newer than the previous compile timestamp. + +1. `flutter_tools` instructs a running Dart compiler to recompile the application and tells it which files have changed. The Dart compiler recompiles. + +1. `flutter_tools` sends the updated files to the device. This includes any changed assets and the newly compiled *delta kernel file* (the output of the compilation, a file that the Dart VM can understand). + +1. `flutter_tools` asks all isolates in the Dart VM on the device to reload their sources (to read the changed delta kernel file and perform its magic). + +1. `flutter_tools` instructs the application on the device to reassemble — to rebuild all widgets on screen, reload assets, redo layout, repaint, etc. + +On my developer machine, with the target device being Linux (that is, a desktop application running locally), performing the first hot reload on a freshly created `flutter create` application after only updating the timestamp on the `lib/main.dart` file I get timings like these (extracted from `flutter run -v`): + +1. Scanning files takes ~13 ms. + +1. Recompiling takes ~67 ms. + +1. Sending files to the device takes ~2 ms. + +1. The Dart VM reloads the sources in ~96 ms. + +1. Reassembling takes ~114 ms. + +If I instead have a bigger application (and change some other file) I might instead get times like these: + +1. Scanning files takes ~12 ms. + +1. Recompiling takes ~386 ms. + +1. Sending files to the device takes ~2 ms. + +1. The Dart VM reloads the sources in ~171 ms. + +1. Reassembling takes ~229 ms. + +In both cases the following steps take the most time: + +* Recompiling + +* Reloading + +* Reassembling + +To make hot reload faster we have to make one or more of those three steps faster. + +Here I’ll focus on the first part: recompiling the changed source files into something that the Dart VM can use. + +## Recompiling + +Logically if I as a user change a file — say, `foo.dart` — I might expect a recompilation to look like this: + +1. The compiler has the old state in memory. + +1. The compiler is told that `foo.dart` has changed. + +1. The compiler throws away its internal state for `foo.dart`. + +1. The compiler recompiles `foo.dart`. + +1. Done. + +This would be great. That would mean that whichever file I change I’d only recompile that file and — presumably — the recompilation would be fast. + +Unfortunately recompilation doesn’t generally work like that. Here are two examples of why recompilation might not be so simple: + +* `foo.dart` used to contain class `Foo`, which is used all over the place. The changed file doesn’t contain this class (maybe it was manually renamed), and every file using the class should get a compile error. + +* `foo.dart` used to have a field defined as `var z = 42`. Another file uses this field: `var z2 = z * 2`. Dart type inference figured out that `z` was an integer and that `z2` was an integer because `z` was one. Now the field changed to say `var z = 42.2`. This time Dart type inference will figure out that the field is a double, but without recompiling the other library as well `z2` will still (incorrectly) be marked as an integer. + +For this reason, a recompilation in Dart has long looked like this: + +1. The compiler has the old state in memory. + +1. The compiler is told that `foo.dart` has changed. + +1. The compiler throws away its internal state for `foo.dart`. + +1. The compiler checks which files import or export `foo.dart` and throws those away too. + +1. The compiler checks which files import or export the files in step 4 and throws those away too. + +1. On and on: throw away all transitive importers and exporters. + +1. The compiler recompiles all (now) “missing” libraries. + +1. Done. + +This might sound bad, but in many cases it isn’t. Although changing your own custom widget set might result in recompiling all the code *you* wrote, it doesn’t result in recompiling the Flutter framework itself, for example, because the Flutter framework doesn’t import or export your libraries. On the other hand, if you change a file that’s central to the Flutter framework, you’ll end up recompiling (almost) everything. + +Looking back at the (incomplete) list of reasons why just recompiling the single changed file doesn’t work, though, we might see a pattern: It doesn’t work because you made *global* changes — changes that influence other libraries. But what if you only changed a comment? Or added another debug-print to your build method? Or fixed an off-by-one bug in your utility method? Those changes aren’t global, and we should be able to do better! + +## Doing better + +For non-global changes — changes that can’t influence the compilation of other libraries — we could in fact recompile only the changed library and still preserve semantics. The main problem is figuring out when a change is global and when it isn’t (and doing so fast). Luckily this can be done in incremental steps: we don’t have to make it perfect right away (or at all). + +A first step might be to compare the file as it was with the file as it is now while ignoring comments in both versions of the file. If, when comparing this way, both versions of the file are equal, we conclude that there were no global changes and we proceed to recompile the single changed file instead of the transitive import-export graph. This technique isn’t perfect. For example, it would still trigger the recompilation of all transitive importers and exporters when fixing that off-by-one bug in your utility method. But it would allow you to fix that spelling mistake in your comments while recompiling only that one file. + +A quick side note here: Why do we have to recompile at all if we’re only changing comments? It’s mostly because of stack traces. Internally some nodes (representing your code) contain *offsets* — information about their placement in the file. If this information becomes out of date, your stack trace contains invalid information. For example, it might claim that something happened at line 42 when really it didn’t. + +To get to a place where you can in fact fix that off-by-one bug in your utility method while still only recompiling that file, we have to ignore one more thing when checking for global changes: function bodies. Again we’ll compare the before and after version of the changed file, this time ignoring both comments and function bodies. If they’re the same we’ll recompile only that file. + +Now we’re actually in a position where you can make a number of useful changes without recompiling more than the file you’ve changed. You can add, remove, and otherwise change comments. You can add (and remove) debug-prints to your build methods. You can even fix bugs in your utility methods. + +## Good news! + +As it turns out these improvements to recompilation have actually been made. If you’re using Flutter 2.2 you might even have noticed it already. If not, maybe you will now. Truth be told, for small applications you likely won’t notice much of a speedup, but for larger applications you should. + +I’ve made a few examples of non-global changes to gauge the effect. + +For the [Veggie Seasons](https://github.com/flutter/samples/tree/master/veggieseasons) sample application (a relatively small application): + +* Changing `lib/main.dart` offers no improvement. It compiled one file before and one file now. + +* Changing `lib/data/veggie.dart` gives improvements in the 30% range. Actual compilation time on my computer goes from 100+ ms to <20 ms (it used to compile 18 files, now only compiles 1 file). This naturally is much more than 30%, but because recompilation was only one out of three timesinks (the other two being reloading and reassembly) the overall change comes out at around 30%. + +For [Flutter Gallery](https://github.com/flutter/gallery) (a relatively large application): + +* Changing `lib/main.dart` yields very minor improvements (it compiles 1 file instead of 2). + +* Changing `lib/layout/adaptive.dart` results in almost halving the reload time. Recompilation time alone goes from almost 400 ms to 40 ms (recompiling 1 file instead of 47 files). + +You should expect real world hot reloads to, on average, be in the order of 30% faster in Flutter 2.2 than they were in Flutter 2.0. To put this in perspective, this change saves Flutter developers, collectively, more than a year of waiting for hot reload every 5 days. + +## Caveats + +Our changes to hot reload don’t always mean that the compiler does less work. For example, if you add or remove a method, the compiler won’t do less work. If you change the initializer of a field, the compiler won’t do less work. If you change the class hierarchy, the compiler won’t do less work. If you change the body of a function — a case where the compiler generally *should* do less work — because of technicalities around mixins and FFI, the compiler might *still* need to do as much work. + +Also, we skipped a couple of technical details when we talked about comparing files. First, we can’t ignore *every* comment: we need to keep the [`@dart` version marker](https://dart.dev/guides/language/evolution#per-library-language-version-selection) because it has semantic meaning. Second, we can’t ignore every function body because of implementation-specific challenges around mixins and FFI. + +## Conclusion + +Hot reload was fast in Flutter 2.0, but it’s faster in Flutter 2.2. On average, hot reload is around 30% faster in Flutter 2.2 than in Flutter 2.0, saving Flutter developers, collectively, more than a year of waiting for hot reload every 5 days. + +If you haven’t updated yet (or haven’t even tried Flutter yet), now might be a good time to visit [flutter.dev](http://flutter.dev) and give it a try. \ No newline at end of file diff --git a/sites/www/content/blog/flutter-in-2022-strategy-and-roadmap/images/1_CcfyhAWUIV8pWwuvdoSPA.webp b/sites/www/content/blog/flutter-in-2022-strategy-and-roadmap/images/1_CcfyhAWUIV8pWwuvdoSPA.webp new file mode 100644 index 0000000000..d10e966f22 Binary files /dev/null and b/sites/www/content/blog/flutter-in-2022-strategy-and-roadmap/images/1_CcfyhAWUIV8pWwuvdoSPA.webp differ diff --git a/sites/www/content/blog/flutter-in-2022-strategy-and-roadmap/index.md b/sites/www/content/blog/flutter-in-2022-strategy-and-roadmap/index.md new file mode 100644 index 0000000000..e17e307bb1 --- /dev/null +++ b/sites/www/content/blog/flutter-in-2022-strategy-and-roadmap/index.md @@ -0,0 +1,28 @@ +--- +title: "Flutter in 2022: strategy and roadmap" +description: "Areas of focus for Flutter as we build growth and momentum" +publishDate: 2022-02-09 +author: timsneath +image: images/1_CcfyhAWUIV8pWwuvdoSPA.webp +category: announcements +layout: blog +--- + +<DashImage figure src="images/1_CcfyhAWUIV8pWwuvdoSPA.webp" /> + + +## Flutter in 2022: strategy and roadmap + +### Areas of focus for Flutter as we build growth and momentum + +Each year we, like other large teams at Google, go through a planning process to set our goals and areas of focus for the upcoming year. Normally, product teams closely guard these documents, since they may contain commercially-sensitive information or relate to areas of competitive advantage. However, as an open source project, we believe that transparency is a virtue: it allows greater trust in our future and direction, and allows others to make plans with better clarity as to how their investments may connect to ours. + +As such, today we’re sharing our [annual strategy doc](https://docs.google.com/document/d/e/2PACX-1vTI9X2XHN_IY8wDO4epQSD1CkRT8WDxf2CEExp5Ef4Id206UOMopkYqU73FvAnnYG6NAecNSDo9TaEO/pub) with the community at large for the first time. In this document, we express our mission, share some guiding principles, and describe the major investments that we plan to undertake in the coming year. By necessity, there are some minor redactions (such as commercially-sensitive data or references to unannounced products), but we believe this provides a good overview of our plans. This strategy document should be read alongside the [engineering roadmap](https://github.com/flutter/flutter/wiki/Roadmap) on our wiki, which adds further specifics around features that we’re working on. + +An important note on the use of the word ‘we’ in the above sentences, which may be read as “those who Google pay to work on Flutter”. Flutter is the work of many contributors inside and outside Google. While Google is the largest sponsor as measured by number of paid contributors, it is not the only contributor. In the last year, thousands of individuals have contributed their intellectual property and labor to the project, ranging from individuals to large corporations such as Canonical, Microsoft, ByteDance, and Alibaba. + +Therefore, while the strategy document describes the investments that Google is making and shares the rationale behind those decisions, it isn’t intended to put boundaries on how others contribute to Flutter. We hope our work is well-aligned with the needs of the community at large, but we anticipate one benefit of sharing this document is that others will see opportunities for their own investments, either by building on work that Google plans to contribute, or by filling areas that we are not prioritizing. + +Some examples of this include the work that Sony is doing on [embedded Linux](https://github.com/sony/flutter-embedded-linux); the investments that Toyota and other automotive companies are making on using Flutter in [Automotive Grade Linux](https://www.youtube.com/watch?v=IUVnmdYBDxk), and work from Samsung and others to port [Flutter to Tizen](https://github.com/flutter-tizen/flutter-tizen). While none of this work is listed in our strategy document, it is part of the Flutter project, just like any code that Google contributes. And of course, there are tens of thousands of packages that build on the framework, the vast majority of which come from developers outside of Google. + +One final disclaimer: our strategy will likely evolve over time as we continue to engage with the community, and as business priorities and strategies change. As a result, nothing here should be seen as representing a binding commitment, even though we plan to follow this as our guide. \ No newline at end of file diff --git a/sites/www/content/blog/flutter-in-2023-strategy-and-roadmap/images/1OMkiWPCxgYijkAvNn-e3MA.webp b/sites/www/content/blog/flutter-in-2023-strategy-and-roadmap/images/1OMkiWPCxgYijkAvNn-e3MA.webp new file mode 100644 index 0000000000..ae0cd6868f Binary files /dev/null and b/sites/www/content/blog/flutter-in-2023-strategy-and-roadmap/images/1OMkiWPCxgYijkAvNn-e3MA.webp differ diff --git a/sites/www/content/blog/flutter-in-2023-strategy-and-roadmap/index.md b/sites/www/content/blog/flutter-in-2023-strategy-and-roadmap/index.md new file mode 100644 index 0000000000..3cdaa47441 --- /dev/null +++ b/sites/www/content/blog/flutter-in-2023-strategy-and-roadmap/index.md @@ -0,0 +1,20 @@ +--- +title: "Flutter in 2023: strategy and roadmap" +description: "A guide to our strategy and areas of investment" +publishDate: 2023-03-30 +author: timsneath +image: images/1OMkiWPCxgYijkAvNn-e3MA.webp +category: announcements +layout: blog +--- + +<DashImage figure src="images/1OMkiWPCxgYijkAvNn-e3MA.webp" /> + + +As an open source project, we believe that we serve our customers best when we are transparent about our roadmap. + +For developers who adopt a technology like Flutter, it’s not enough for a product to merely offer a useful set of features. A dependency on Flutter is also a long-term commitment to maintain a skillset and codebase. For that reason, it’s important that we articulate a vision and direction that is compelling and realistic. We hope that sharing more about why we (Google) invest in Flutter will give you greater trust in our future and direction, and allow you to plan with better clarity as to how your investments might connect or supplement ours. + +**We’re therefore sharing [our 2023 strategy document today](https://flutter.dev/go/strategy-2023)**, where we express our statement of purpose and guiding principles, and describe the major investments that we plan to undertake through the remainder of this year. By necessity, there are some minor redactions (such as commercially-sensitive data or references to unannounced products), and like all plans, we don’t expect this to survive first contact with reality. This strategy document should be read alongside the [engineering roadmap](https://github.com/flutter/flutter/wiki/Roadmap) on our wiki, which adds further specifics around features that we’re working on. + +In closing, an important note on the use of the word ‘we’ in the above sentences, which might be read as “those who Google pay to work on Flutter”. We hope that the Flutter contributors remain far more numerous than just those who are employed by Google, but we don’t claim to speak for the incentives that others have or the work that they might independently undertake. We’re unendingly grateful for this collaboration. \ No newline at end of file diff --git a/sites/www/content/blog/flutter-live-what-to-expect-from-googles-event/images/0AyHpMwfNETHD93mV.webp b/sites/www/content/blog/flutter-live-what-to-expect-from-googles-event/images/0AyHpMwfNETHD93mV.webp new file mode 100644 index 0000000000..422a483952 Binary files /dev/null and b/sites/www/content/blog/flutter-live-what-to-expect-from-googles-event/images/0AyHpMwfNETHD93mV.webp differ diff --git a/sites/www/content/blog/flutter-live-what-to-expect-from-googles-event/images/0MhKKsmIlYcORAT-q.webp b/sites/www/content/blog/flutter-live-what-to-expect-from-googles-event/images/0MhKKsmIlYcORAT-q.webp new file mode 100644 index 0000000000..ee3b163e04 Binary files /dev/null and b/sites/www/content/blog/flutter-live-what-to-expect-from-googles-event/images/0MhKKsmIlYcORAT-q.webp differ diff --git a/sites/www/content/blog/flutter-live-what-to-expect-from-googles-event/images/0S2k_EeW-eFwWQwbu.webp b/sites/www/content/blog/flutter-live-what-to-expect-from-googles-event/images/0S2k_EeW-eFwWQwbu.webp new file mode 100644 index 0000000000..aaaa0dc0ad Binary files /dev/null and b/sites/www/content/blog/flutter-live-what-to-expect-from-googles-event/images/0S2k_EeW-eFwWQwbu.webp differ diff --git a/sites/www/content/blog/flutter-live-what-to-expect-from-googles-event/images/0j1UikCUFWQNp5x0A.webp b/sites/www/content/blog/flutter-live-what-to-expect-from-googles-event/images/0j1UikCUFWQNp5x0A.webp new file mode 100644 index 0000000000..21939bd73b Binary files /dev/null and b/sites/www/content/blog/flutter-live-what-to-expect-from-googles-event/images/0j1UikCUFWQNp5x0A.webp differ diff --git a/sites/www/content/blog/flutter-live-what-to-expect-from-googles-event/images/0rv1oAlPBPjcbMwrb.webp b/sites/www/content/blog/flutter-live-what-to-expect-from-googles-event/images/0rv1oAlPBPjcbMwrb.webp new file mode 100644 index 0000000000..0b20f023c7 Binary files /dev/null and b/sites/www/content/blog/flutter-live-what-to-expect-from-googles-event/images/0rv1oAlPBPjcbMwrb.webp differ diff --git a/sites/www/content/blog/flutter-live-what-to-expect-from-googles-event/index.md b/sites/www/content/blog/flutter-live-what-to-expect-from-googles-event/index.md new file mode 100644 index 0000000000..39610969b7 --- /dev/null +++ b/sites/www/content/blog/flutter-live-what-to-expect-from-googles-event/index.md @@ -0,0 +1,54 @@ +--- +title: "Flutter Live — What to Expect from Google’s event" +description: "tl;dr: Flutter Live, a celebration of Google’s new mobile app SDK, is happening on December 4th. Watch it at g.co/FlutterLive." +publishDate: 2018-12-02 +author: csells +image: images/0MhKKsmIlYcORAT-q.webp +category: events +layout: blog +--- + +## Flutter Live — What to Expect from Google’s event + +<DashImage figure src="images/0MhKKsmIlYcORAT-q.webp" /> + + +## What to expect at Flutter Live + +[Flutter Live](https://developers.google.com/events/flutter-live/) is happening on December 4th, with a small in-person event at the Science Museum in London, and a large global livestream audience. This event is a celebration of [Flutter](http://flutter.io), Google’s free and open source SDK for building high-quality native iOS and Android apps from a single codebase. + +<YoutubeEmbed id="M71JIWWV_Q0" title="Announcing Flutter Live!" fullwidth="true"/> + + +## Tune in to the livestream + +Go to [g.co/FlutterLive](http://g.co/FlutterLive) and [sign up for livestream updates](https://docs.google.com/forms/d/e/1FAIpQLSc95VLm0odas0ieZ7-6K-5B95piV-SfgVvVxVwV39tZl_iSHw/viewform). The event will be broadcasted on the website Dec 4 with a pre-show starting at [4:00 p.m. GMT](https://www.timeanddate.com/worldclock/converter.html?iso=20181204T160000&p1=136&p2=137&p3=179&p4=213&p5=37&p6=771&p7=33) and the keynote at [5:00 p.m. GMT](https://www.timeanddate.com/worldclock/converter.html?iso=20181204T170000&p1=136&p2=137&p3=179&p4=213&p5=37&p6=771&p7=33). + +## Viewing Parties + +Over 130 Organizations all over the world are hosting free viewing parties to watch and discuss Flutter Live. Find a viewing party happening near you; [here is the full list](https://developers.google.com/events/flutter-live/viewing-parties/). + +<DashImage figure src="images/0j1UikCUFWQNp5x0A.webp" /> + + +## #MyFlutterStory + +Developers around the world have been submitting their Flutter stories, and we will highlight some at the event. + +<DashImage figure src="images/0S2k_EeW-eFwWQwbu.webp" /> + + +<DashImage figure src="images/0AyHpMwfNETHD93mV.webp" /> + + +<DashImage figure src="images/0rv1oAlPBPjcbMwrb.webp" /> + + +## #AskFlutter + +Given this is a global event, Andrew from our team will be live after the Keynote to answer questions that come in with #AskFlutter. + +<YoutubeEmbed id="drNPMrdmqMQ" title="#AskFlutter at Flutter Live - Ask us your questions!" fullwidth="true"/> + + +We are thrilled that you will experience Flutter Live with us on December 4th. In the meantime, follow us on twitter at [@flutterio](https://twitter.com/flutterio) and get started with Flutter at [flutter.io](http://flutter.io). \ No newline at end of file diff --git a/sites/www/content/blog/flutter-on-raspberry-pi-mostly-from-scratch/images/1K1lyX_AOOILKyv3stAtlQg.jpeg b/sites/www/content/blog/flutter-on-raspberry-pi-mostly-from-scratch/images/1K1lyX_AOOILKyv3stAtlQg.jpeg new file mode 100644 index 0000000000..0cf07531f8 Binary files /dev/null and b/sites/www/content/blog/flutter-on-raspberry-pi-mostly-from-scratch/images/1K1lyX_AOOILKyv3stAtlQg.jpeg differ diff --git a/sites/www/content/blog/flutter-on-raspberry-pi-mostly-from-scratch/index.md b/sites/www/content/blog/flutter-on-raspberry-pi-mostly-from-scratch/index.md new file mode 100644 index 0000000000..a1b6435b58 --- /dev/null +++ b/sites/www/content/blog/flutter-on-raspberry-pi-mostly-from-scratch/index.md @@ -0,0 +1,182 @@ +--- +title: "Flutter on Raspberry Pi (mostly) from Scratch" +description: "This document describes an experiment in porting the Flutter engine to a target that is currently not supported. That fact that I used a…" +publishDate: 2018-11-30 +author: chinmaygarde +image: images/1K1lyX_AOOILKyv3stAtlQg.jpeg +category: spotlight +layout: blog +--- + +## Flutter on Raspberry Pi (mostly) from scratch + +*This document describes an experiment in porting the Flutter engine to a target that is currently not supported. The fact that I used a Raspberry Pi is purely incidental. I have deliberately made no assumptions about the target. It is entirely possible that you already have a toolchain ready to use with your specific board. If so, large parts of this document can be skipped. This document also assumes that you are familiar with setting up your own toolchains and build environments.* + +<DashImage figure src="images/1K1lyX_AOOILKyv3stAtlQg.jpeg" alt="Flutter on Raspberry Pi" caption="Flutter on Raspberry Pi" /> + + +Bringing Flutter to a new platform requires porting the [Flutter Engine](https://github.com/flutter/engine) to the target, as well as implementing an embedder. The Flutter engine is inherently cross platform and can work with a variety of client rendering APIs (OpenGL, Vulkan and Software rendering). It includes the Dart runtime VM. + +The engine is typically packaged as a dynamic library and consumed by thin embedders that are platform specific. It has a small C API. Engine developers guarantee that this API is backwards compatible and has a stable [ABI](https://en.wikipedia.org/wiki/Application_binary_interface) moving forward. The API consists of a [single C header](https://github.com/flutter/engine/blob/436f9707b94774d1d049c04b8cda9d81d85aa4a8/shell/platform/embedder/embedder.h) file with no platform specific dependencies. + +It is the job of the embedder to set up the rendering surface, wire up input events, and provide access to Dart application snapshots. For reference, the [Flutter Desktop Embedding](https://github.com/google/flutter-desktop-embedding) project uses this approach to set up Flutter on Mac, Linux, and Windows Desktop environments. + +So, to port Flutter to the Raspberry Pi, you will need the following items: + +* The Flutter engine built for ARMv7. + +* A Raspberry Pi specific embedder that sets up the rendering surface and wires up input events. + +* A snapshot of the Flutter Dart application. You’ll be using the Flutter engine with a JIT mode VM. + +### Constraints + +To demonstrate all the steps necessary in porting Flutter to a new platform, some constraints were placed on the porting process: + +* The Raspberry Pi is capable of running a full desktop environment with fairly heavyweight dependencies like X11. But, instead of depending on a heavyweight desktop environment (where [Flutter Desktop Embedding’s](https://github.com/chinmaygarde/flutter-desktop-embedding) embedder can be used), you will use Broadcom APIs directly. This allows booting directly to Flutter in a lightweight environment with no dependencies you don’t need. + +* You won’t use existing toolchains for building any artifacts. The Raspberry Pi is a fairly popular platform with well supported toolchains, but this process follows the same steps required when porting for a more esoteric platform without good toolchain support. + +* You should be able to perform all builds on a powerful workstation. Building native artifacts on the Raspberry Pi is possible, but prohibitively time consuming. + +## Building the Flutter Engine + +### The Toolchain + +To build the Flutter engine for the Raspberry Pi, you need a toolchain capable of compiling engine code to ARMv7. The target toolchain is ***`arm-linux-gnueabihf`***. The engine developers rely on a toolchain that is very close to Tip-of-Tree of [Clang/LLVM](https://clang.llvm.org) for their development. While using a modern version of GCC is certainly possible, the engine developers are unlikely to accept patches that are GCC specific without an ongoing investment in the requisite build and test infrastructure for that compiler. So, do as the engine developers do on supported platforms, and set up a Clang/LLVM toolchain that is at Tip-of-Tree. + +Preparing a Clang toolchain for ***`arm-linux-gnueabihf`*** is fairly straightforward. It involves downloading the source code as described on the [getting started page for Clang/LLVM](https://clang.llvm.org/get_started.html). It is important to not check out ***`libcxx`*** and ***`libcxxabi`*** as described in the “optional” sections of that document. These libraries will not be built for the target platform. Instead, they will install host artifacts at the location specified by the installation prefix. + +Once the sources are checked out, build the toolchain with the following flags: + +```shell +cmake ../llvm \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/sdk/toolchain \ + -DLLVM_DEFAULT_TARGET_TRIPLE=arm-linux-gnueabihf \ + -DLLVM_TARGETS_TO_BUILD=ARM +``` + +Once, you have the Clang/LLVM toolchain, you need Binutils built for ***`arm-linux-gnueabihf`***. The engine developers use Gold as the linker on Linux, so do the same. The Binutils source code can be downloaded from [the Git repository mentioned on the homepage for Binutils](https://www.gnu.org/software/binutils/). Configure the build with the following flags: + +```shell +./configure --prefix="/sdk/toolchain" \ + --enable-gold \ + --enable-ld \ + --target=arm-linux-gnueabihf +``` + +You now have a toolchain capable of building for (and only for) ***arm`-linux-gnueabihf`***. If you already have a toolchain that is fairly close to Tip-of-Tree Clang that is also capable of targeting this platform, you can use that. But, this toolchain gives you a better chance of avoiding compiler specific issues due to subtle version mismatches. + +### **libcxx** and libcxxabi + +You can take the brand new toolchain for a spin by using it to build ***`libcxx`*** and ***`libcxxabi`***. The [Raspberry Pi Rasbian](https://www.raspberrypi.org/downloads/raspbian/) image has ***`libstdc++`*** that is rather old and not fully suitable for use by the engine. Besides, you want the engine to be self contained and not depend on a C++ library implementation on the target. You are going to use a ***`libcxx`*** and link it statically into the engine. That way, the engine has everything it needs to run with minimal dependencies on the underlying platform. Since you are building ***`libcxx`*** for use only by the engine, you can disable support for ***exceptions*** and ***RTTI***, which the engine doesn’t need. + +Download the source code for ***`libcxx`*** and ***`libcxxabi`*** as [described on the “Building Libcxx” page from the LLVM team](https://libcxx.llvm.org/docs/BuildingLibcxx.html). Given the specific features of ***`libcxx`*** described above, run the following commands. + +**Note**: If you want more help with any CMake variable, the following commands are extremely useful: + +```shell +cmake --help-variable CMAKE_SYSROOT_COMPILE +cmake --help-variable-list +``` + +Build ***`libcxxabi`*** first, as ***`libcxx`*** depends on artifacts generated by the ***`libcxxabi`*** target: + +```shell +cmake ../llvm/projects/libcxxabi \ + -DCMAKE_CROSSCOMPILING=True \ + -DCMAKE_SYSROOT=/sdk/sysroot \ + -DLIBCXX_ENABLE_SHARED=False \ + -DCMAKE_INSTALL_PREFIX=/sdk/toolchain \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_SYSTEM_NAME=Linux \ + -DCMAKE_SYSTEM_PROCESSOR=ARM \ + -DCMAKE_C_COMPILER=/sdk/toolchain/bin/clang \ + -DCMAKE_CXX_COMPILER=/sdk/toolchain/bin/clang++ \ + -DLLVM_TARGETS_TO_BUILD=ARM \ + -DLIBCXXABI_ENABLE_EXCEPTIONS=False +``` + +Then, build ***libcxx***. Notice that it is configured for static linking: + +```shell +cmake ../llvm/projects/libcxx \ + -DCMAKE_CROSSCOMPILING=True \ + -DCMAKE_SYSROOT=/sdk/sysroot \ + -DLIBCXX_ENABLE_SHARED=False \ + -DCMAKE_INSTALL_PREFIX=/sdk/toolchain \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_SYSTEM_NAME=Linux \ + -DCMAKE_SYSTEM_PROCESSOR=ARM \ + -DCMAKE_C_COMPILER=/sdk/toolchain/bin/clang \ + -DCMAKE_CXX_COMPILER=/sdk/toolchain/bin/clang++ \ + -DLLVM_TARGETS_TO_BUILD=ARM \ + -DLIBCXX_ENABLE_EXCEPTIONS=False \ + -DLIBCXX_ENABLE_RTTI=False \ + -DLIBCXX_CXX_ABI=libcxxabi \ + -DLIBCXX_CXX_ABI_INCLUDE_PATHS=/sdk/toolchain/include/c++/v1 \ + -DLIBCXX_CXX_ABI_LIBRARY_PATH=/sdk/toolchain/lib \ + -DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=True +``` + +Once the install step is done for both libraries, look inside the path specified in the install prefix to ensure that no dynamic libraries for ***`libcxx`*** or ***`libcxxabi`*** ended up there. You want to be absolutely sure you don’t create artifacts that depend on these dynamic libraries (because they won’t actually be present on the target). If you find any, remove them. + +Copy the ***`cxxabi.h`*** and ***`__cxxabi_config.h`*** headers to ***c++/v1*** in the install prefix folder. These aren’t placed there by default. + +### The Sysroot + +From a fresh headless Raspbian installation, ***`rsync`*** the ***`lib`***, ***`opt`***, and ***`usr`*** directories to a single directory on your host. We refer to this directory as the **`sysroot`** and use it to build artifacts on the host that link with system libraries on the target (the Raspberry Pi). Feel free to trim the contents of these directories to exclude dependencies you won’t need in the embedder itself. + +To build the engine, you only need ***`libdl`***, ***`libpthread`***, ***`librt`***, ***`libm`*** and ***`libc`***, and their headers. The embedder implementation will also likely need to link against the Broadcom libraries (these are in ***`opt`***). + +### The Flutter Engine + +Set up your host environment for the Flutter engine using the instructions in [the contributing guide](https://github.com/flutter/engine/blob/master/CONTRIBUTING.md). Make sure you are on a Linux desktop host as you are building for a Linux target (the Raspberry Pi) but with a custom toolchain (the one you just built), and ***`sysroot`*** (the one you just pulled via ***`rsync`***). + +After your build root is prepared, build using the following GN flags: + +```shell +./flutter/tools/gn \ + --target-sysroot /sdk/sysroot \ + --target-toolchain /sdk/toolchain \ + --target-triple arm-linux-gnueabihf \ + --linux-cpu arm \ + --runtime-mode debug \ + --embedder-for-target \ + --no-lto \ + --target-os linux \ + --arm-float-abi hard \ +``` + +You’ve generated ***`libflutter_engine.so`***. Grab ***`icudtl.dat`*** and the ***`flutter_embedder.h`*** header. You need these artifacts to build the embedder in the next step. + +## Building the Embedder + +You may build the embedder using whatever build system you are most comfortable with. To accompany this document, a sample embedder was built with the GN build system based on the [Raspberry Pi Cross Compilation SDK](https://github.com/chinmaygarde/raspberrypi_cross_compilation_sdk) (a generic build root and toolchain capable of cross-compiling C/C++ applications for the Raspberry Pi). This embedder implements rendering using Broadcom APIs. You can find the embedder at [this GitHub repository](https://github.com/chinmaygarde/flutter_from_scratch). It looks very similar to desktop embedder implementations but is much simpler. It could serve as an unopinionated started off point for your own implementations. + +Push the build artifacts to the target Raspberry Pi device. + +## Building the Flutter Application Snapshot + +On the host, check out the application you want to run on the Raspberry Pi, and build the application bundle using the Flutter tools. This can be done with the following command: + +``` +flutter build bundle +``` + + +Push this bundle to the Raspberry Pi. + +## Run the Application on the Raspberry Pi + +In the specific case of the Raspberry Pi Embedder, run the application using the following invocation: + +``` +flutter /path/to/flutter_assets/ +``` + + +Automate this process to your desired workflow. + +**Congratulations! You’ve ported Flutter to a Raspberry Pi! Now comes the hard bit of making the port production ready.** \ No newline at end of file diff --git a/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0-P_8Vyu4EMCZ4ji0.webp b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0-P_8Vyu4EMCZ4ji0.webp new file mode 100644 index 0000000000..9597b34bbe Binary files /dev/null and b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0-P_8Vyu4EMCZ4ji0.webp differ diff --git a/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/08WXtO7Mx_n5Gs0m_.webp b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/08WXtO7Mx_n5Gs0m_.webp new file mode 100644 index 0000000000..f36539dadc Binary files /dev/null and b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/08WXtO7Mx_n5Gs0m_.webp differ diff --git a/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0C0d4X2FddTe0fC6A.webp b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0C0d4X2FddTe0fC6A.webp new file mode 100644 index 0000000000..289d551590 Binary files /dev/null and b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0C0d4X2FddTe0fC6A.webp differ diff --git a/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0R7aSejQwaxUY2W9L.webp b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0R7aSejQwaxUY2W9L.webp new file mode 100644 index 0000000000..a044906b93 Binary files /dev/null and b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0R7aSejQwaxUY2W9L.webp differ diff --git a/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0WdHGeM6kI6hqtQGw.webp b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0WdHGeM6kI6hqtQGw.webp new file mode 100644 index 0000000000..7e81aa751b Binary files /dev/null and b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0WdHGeM6kI6hqtQGw.webp differ diff --git a/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0YB3J0hC7A6F7NdXE.webp b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0YB3J0hC7A6F7NdXE.webp new file mode 100644 index 0000000000..dd234c310e Binary files /dev/null and b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0YB3J0hC7A6F7NdXE.webp differ diff --git a/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0YNnoQE06DVStrvWV.webp b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0YNnoQE06DVStrvWV.webp new file mode 100644 index 0000000000..5af5fedde4 Binary files /dev/null and b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0YNnoQE06DVStrvWV.webp differ diff --git a/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0_CopRAy_n9COQGqR.webp b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0_CopRAy_n9COQGqR.webp new file mode 100644 index 0000000000..e4cf2878a4 Binary files /dev/null and b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0_CopRAy_n9COQGqR.webp differ diff --git a/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0a-qk7SfiAnHAq2vU.webp b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0a-qk7SfiAnHAq2vU.webp new file mode 100644 index 0000000000..6305b2e6b7 Binary files /dev/null and b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0a-qk7SfiAnHAq2vU.webp differ diff --git a/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0k_YiDB0OZTd8WQI2.webp b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0k_YiDB0OZTd8WQI2.webp new file mode 100644 index 0000000000..55e56df767 Binary files /dev/null and b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0k_YiDB0OZTd8WQI2.webp differ diff --git a/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0mTLE89U4VUtGDnEI.webp b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0mTLE89U4VUtGDnEI.webp new file mode 100644 index 0000000000..6b4a602010 Binary files /dev/null and b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/images/0mTLE89U4VUtGDnEI.webp differ diff --git a/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/index.md b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/index.md new file mode 100644 index 0000000000..c9e18892d7 --- /dev/null +++ b/sites/www/content/blog/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020/index.md @@ -0,0 +1,114 @@ +--- +title: "Flutter on the web, slivers, and platform-specific issues: user survey results from Q3 2020" +description: "Written by the Flutter UXR team (JaYoung Lee, Youyang Hou, Jack Kim, Tao Dong)" +publishDate: 2020-10-20 +author: jayoung-lee +image: images/0_CopRAy_n9COQGqR.webp +category: announcements +layout: blog +--- + +In August 2020, the Flutter team launched its 10ᵗʰ quarterly user survey. Over 10 days, 7,668 users responded to the survey globally. Each user spent about 7.4 minutes to complete the survey, which equals 39.4 days of developer time. We deeply appreciate the time everyone took to provide feedback to us, and we want to share the results with you. + +Like we always do, we asked about your level of satisfaction with various parts of Flutter. This quarter, we also focused on getting feedback about additional topics, such as Flutter on the web, slivers (widgets for rich scrolling effects), and debugging platform-specific issues. We cover each of the topics in more detail in this article. If you’re interested, read on! + +**Summary** + +* 94% of the respondents were positively satisfied (PSAT) with Flutter overall, and 58% were very satisfied (VSAT). While PSAT is stable, VSAT is increasing continuously. + +* The percentage of users working for enterprise companies increased from 26% to 31%. The proportion of advanced users is increasing as well. + +* 59% of those who worked with Flutter on the web in the last 3 months were positively satisfied with the performance of it. 71% were positively satisfied with Flutter’s ability to create a UI that feels natural on the web. + +* 79% of the users who wanted to implement rich scrolling effects with slivers tried using them. The biggest issue (36%) was difficulty finding the widget that met their needs. + +* 71% of the users have experience debugging platform-specific issues. The common issues were with tooling (32%), visual differences (28%), and dependency management issues (28%). + +## Satisfaction and changes in user base + +The percentage of users that were *very satisfied* with Flutter hit a record high at 58% this quarter. Overall, Flutter users continued to be satisfied with the product (94%), even with the exponential growth of the community. The following graph shows how the level of satisfaction with Flutter has progressed over time. + +<DashImage figure src="images/0k_YiDB0OZTd8WQI2.webp" alt="*58% of the users selected “Very satisfied” for the question asking, “Overall, how satisfied are you with Flutter?”*" caption="*58% of the users selected “Very satisfied” for the question asking, “Overall, how satisfied are you with Flutter?”*" /> + + +There were some notable changes to our user base. First, the percentage of users working for enterprise companies increased significantly to 31%, from 26% in Q1 and Q2, while the percentage of users working for start-ups remained stable at around 35% during this period. + +<DashImage figure src="images/0mTLE89U4VUtGDnEI.webp" alt="*While most of the Flutter users are working for start-ups, the percentage of developers working for enterprises increased significantly from 26% to 31%.*" caption="*While most of the Flutter users are working for start-ups, the percentage of developers working for enterprises increased significantly from 26% to 31%.*" /> + + +Another notable change was in the perceived level of experience with Flutter. As you can see in the following graph, the proportion of novice users decreased over time, while the proportion of advanced users increased over time. This means that we have more experienced users in our community who can help share their knowledge with novice users. If you’re interested in gaining or sharing knowledge, you can participate in online conversations with other Flutter developers. Visit the [Community tab](https://flutter.dev/community) on flutter.dev. + +<DashImage figure src="images/0_CopRAy_n9COQGqR.webp" alt="*The proportion of advanced users is increasing continuously.*" caption="*The proportion of advanced users is increasing continuously.*" /> + + +## Flutter on the web + +Flutter supports web development in beta, in addition to supporting mobile in the stable channel. In this survey, we asked about specific issues early adopters had regarding performance, web-like experience, workflows, and documentation, so that we can prioritize important issues. + +Survey respondents were actively using Flutter to build web apps. About 33% said they have either evaluated Flutter on the web for potential production use (15%), have built a demo (11%), or published a production app (7%), as shown in the third row of the following graph. + +<DashImage figure src="images/0R7aSejQwaxUY2W9L.webp" alt="*33% of the users (1,468 out of 4,449) reported that they have either evaluated Flutter on the web for production (15%), have built a demo* (11%), *or published a production app (7%).*" caption="*33% of the users (1,468 out of 4,449) reported that they have either evaluated Flutter on the web for production (15%), have built a demo* (11%), *or published a production app (7%).*" /> + + +More specifically, 29.5% of the users who had both mobile and web experience used Flutter on the web more seriously (for potential production) in the last 3 months. The percentage varied depending on the developers’ prior experience with the platforms. It appears that the respondents with web-only backgrounds were trying Flutter as an alternative web framework (22% tried Flutter on the web for potential production), and the respondents with mobile-only backgrounds were actively trying Flutter on the web as a gateway to web development (16% tried Flutter on the web). + +<DashImage figure src="images/0C0d4X2FddTe0fC6A.webp" alt="*The percentage of users who tried Flutter on the web for production in the last 3 months varied depending on their prior experience with mobile and web platforms.*" caption="*The percentage of users who tried Flutter on the web for production in the last 3 months varied depending on their prior experience with mobile and web platforms.*" /> + + +The Flutter web team also collected feedback regarding various issues around creating a web-like experience. First, we learned that the respondents found browser navigation and routing history (55%), copying/pasting selected text (34%), scrolling physics (33%), and selecting text (32%) as the most critical in creating a web-like experience. Respondents also asked for better documentation for routing and adapting mobile layouts to the web. + +<DashImage figure src="images/08WXtO7Mx_n5Gs0m_.webp" alt="*Users selected “browser navigation and routing history” as the most critical feature in creating a web-like experience using Flutter.*" caption="*Users selected “browser navigation and routing history” as the most critical feature in creating a web-like experience using Flutter.*" /> + + +The Flutter team is actively making improvements based on the feedback we receive. To address the top user issues around navigation and routing, the team recently released [Navigator 2](https://medium.com/flutter/learning-flutters-new-navigation-and-routing-system-7c9068155ade). We also added support for [copying/pasting selectable text](https://github.com/flutter/flutter/issues/47234), and have plans to improve the [selectable text](https://github.com/flutter/flutter/projects/173#card-43620465) functionality (especially for rich text). We are continuing our efforts to enhance scrolling physics and performance based on issues reported by the community. + +Finally, the team received feedback around workflows, performance, and third-party APIs. Within basic workflows, respondents found debugging the most difficult. Page loading speed and scrolling were the most common performance issues respondents ran into. Respondents wanted Flutter on the web to have better support for local storage (for example, SQLite), Firebase storage, and Google Maps. These areas will shape up as the team makes progress in Flutter on the web. + +## Slivers + +Sliver widgets (widgets that start with “Sliver” such as SliverAppBar and SliverList) are used to create rich scrolling effects. While many scrolling effects can be achieved with widgets like ListView, GridView, PageView, or AnimatedList, sliver widgets help customize the scroll view and achieve a more beautiful UI. + +<DashImage figure src="images/0a-qk7SfiAnHAq2vU.webp" alt="*You can achieve rich scrolling effects like this with sliver widgets.*" caption="*You can achieve rich scrolling effects like this with sliver widgets.*" /> + + +The Flutter team has heard different stories about users’ experience with slivers. Some said that they don’t use slivers in their apps, and some said that they need more slivers to implement various effects. So, the team decided to add questions to this quarter’s survey, to better understand how Flutter users work with slivers. + +The first thing we learned was that more users design their UIs with simple scrolling effects (49%) than with rich scrolling effects (39%). (An example of a simple scrolling effect and an example of a rich scrolling effect were presented in the survey.) For those who need rich scrolling effects, 78% said that they need slivers to implement the effects they want. + +<DashImage figure src="images/0YNnoQE06DVStrvWV.webp" alt="*More users design their UIs with simple scrolling effects (49%) than with rich scrolling effects (39%).*" caption="*More users design their UIs with simple scrolling effects (49%) than with rich scrolling effects (39%).*" /> + + +Out of those who needed slivers, 20% said that they haven’t tried slivers. What’s more interesting to us is that 35% of the users who said that they tried to use slivers had issues with slivers. When we asked about the biggest issues for them, as shown in the following graph, the discovery issue was the biggest (36%), followed by the learning issue (30%), and then the usability issue (19%). + +<DashImage figure src="images/0-P_8Vyu4EMCZ4ji0.webp" alt="*Discovery was the biggest issue for sliver users, followed by the learning issue, and the usability issue.*" caption="*Discovery was the biggest issue for sliver users, followed by the learning issue, and the usability issue.*" /> + + +Because we do not want you to compromise your UI as a consequence of these issues, we plan to update [flutter.dev](https://flutter.dev/), so that the sliver widgets and relevant information about slivers are easier to find and easier to learn. If you’re looking for new slivers that aren’t in the Flutter framework, consider using community packages, such as [sliver_tools](https://pub.dev/packages/sliver_tools) or [sticky_headers](https://pub.dev/packages/sticky_headers). The Flutter community always welcomes your contribution to this area as well. + +In the meantime, if you’re new to slivers and want to learn more, visit the following resources: + +* [Slivers, demystified](https://medium.com/flutter/slivers-demystified-6ff68ab0296f) (Medium article) + +* [Slivers explained — making dynamic layouts](https://www.youtube.com/watch?v=Mz3kHQxBjGg) (The Boring Flutter Development Show on YouTube, Ep. 12) + +## Debugging platform-specific issues + +In the [previous survey](https://medium.com/flutter/what-are-the-important-difficult-tasks-for-flutter-devs-q1-2020-survey-results-a5ef2305429b), we found that *troubleshooting platform specific issues* and *testing apps across platforms* are the most difficult tasks for Flutter developers. Although we saw some bugs related to platform-specific issues, we didn’t know the relative priorities of the issues and if they’re bundled in themes or spread out. To find the true reasons why troubleshooting and testing are difficult to perform, we added questions that ask what specific issues users encountered. + +First, we asked which platform-specific issues users debugged. We found the most *common* platform-specific issues are tooling issues (32%), visual differences in different platforms (28%), dependency management issues (28%), behavioral differences in different platforms (27%), plugin behavior differences in different platforms (26%), and missing native features (25%). + +<DashImage figure src="images/0YB3J0hC7A6F7NdXE.webp" alt="*The most common platform-specific issues are tooling issues, visual differences, and dependency management issues.*" caption="*The most common platform-specific issues are tooling issues, visual differences, and dependency management issues.*" /> + + +Survey respondents also rated the importance and difficulty of each of the issues they debugged. The most *important* issues to debug are missing native features, performance discrepancies, behavioral differences, plugin behavior differences, and release issues. The most *difficult* issues to debug are missing native features, device-specific issues, performance discrepancies, dependency management, and tooling issues. + +<DashImage figure src="images/0WdHGeM6kI6hqtQGw.webp" alt="*The importance and difficulty of platform-specific issues. (The error bar represents the 95% confidence interval.)*" caption="*The importance and difficulty of platform-specific issues. (The error bar represents the 95% confidence interval.)*" /> + + +Regarding testing apps across multiple platforms, 85% of the respondents said that it’s very or extremely *important*. Yet, it was very or extremely *difficult* for 27% of the respondents. So, testing apps across multiple platforms is important, but most developers don’t feel it’s a big blocker for them. From the open-ended comments, we learned that the most common testing issues included difficulty testing for iOS (especially for Windows developers), testing for multiple screen sizes, and testing on multiple physical devices. + +Our team will invest more time in the next quarter to aggressively reduce our bug queue and address major issues around tooling, dependency management, and release. We will also keep improving documentation around those areas. For example, we will improve documentation about how to use such as CI tools such as (for example, GitHub Actions and Bitrise) that help users to better test their to test apps on iOS. + +## What’s next? + +Your input influences our decision-making every day. We will continue this tradition of basing our decisions on what you tell us in these surveys. So, please, continue to participate and share your thoughts. Flutter’s UX research team is also running a wide variety of user experience (UX) studies, to make your experience with Flutter more pleasant. If you are interested in participating in a study, [sign up](https://docs.google.com/forms/d/e/1FAIpQLSe0i4De809KXVCdljGKrjMj3lxhuzbuFKCtY5PEQPCYtGxFMg/viewform) to be considered for a future study. \ No newline at end of file diff --git a/sites/www/content/blog/flutter-outline-hot-reload-and-the-implementation-of-a-live-widget-preview/images/0NeOjBxMN6kT43xJ7.webp b/sites/www/content/blog/flutter-outline-hot-reload-and-the-implementation-of-a-live-widget-preview/images/0NeOjBxMN6kT43xJ7.webp new file mode 100644 index 0000000000..cd96a5dfb0 Binary files /dev/null and b/sites/www/content/blog/flutter-outline-hot-reload-and-the-implementation-of-a-live-widget-preview/images/0NeOjBxMN6kT43xJ7.webp differ diff --git a/sites/www/content/blog/flutter-outline-hot-reload-and-the-implementation-of-a-live-widget-preview/images/0TUzb7DjU9HweZAAI.webp b/sites/www/content/blog/flutter-outline-hot-reload-and-the-implementation-of-a-live-widget-preview/images/0TUzb7DjU9HweZAAI.webp new file mode 100644 index 0000000000..787663c600 Binary files /dev/null and b/sites/www/content/blog/flutter-outline-hot-reload-and-the-implementation-of-a-live-widget-preview/images/0TUzb7DjU9HweZAAI.webp differ diff --git a/sites/www/content/blog/flutter-outline-hot-reload-and-the-implementation-of-a-live-widget-preview/images/1Zut6fVhPFiAPFp2Uq24H6Q.webp b/sites/www/content/blog/flutter-outline-hot-reload-and-the-implementation-of-a-live-widget-preview/images/1Zut6fVhPFiAPFp2Uq24H6Q.webp new file mode 100644 index 0000000000..d59450e1c7 Binary files /dev/null and b/sites/www/content/blog/flutter-outline-hot-reload-and-the-implementation-of-a-live-widget-preview/images/1Zut6fVhPFiAPFp2Uq24H6Q.webp differ diff --git a/sites/www/content/blog/flutter-outline-hot-reload-and-the-implementation-of-a-live-widget-preview/index.md b/sites/www/content/blog/flutter-outline-hot-reload-and-the-implementation-of-a-live-widget-preview/index.md new file mode 100644 index 0000000000..9de36aff18 --- /dev/null +++ b/sites/www/content/blog/flutter-outline-hot-reload-and-the-implementation-of-a-live-widget-preview/index.md @@ -0,0 +1,136 @@ +--- +title: "Flutter Outline, Hot Reload, and the implementation of a live widget preview" +description: "In M22 of the Flutter plugin for IntelliJ we added a new tool window — the Flutter Outline view. It shows you the structure of your Dart…" +publishDate: 2018-04-04 +author: scheglov +image: images/0TUzb7DjU9HweZAAI.webp +category: announcements +layout: blog +--- + +In M22 of the Flutter plugin for IntelliJ we added a new tool window — the Flutter Outline view. It shows you the structure of your Dart files — the classes, fields, and methods — just like the standard IntelliJ Structure tool window. But when the selected file contains Flutter UI code, it also shows the widget hierarchy — the association between parent and child widgets: + +<DashImage figure src="images/0TUzb7DjU9HweZAAI.webp" /> + + +## What’s new? + +Now with M23, we’ve introduced a new experimental feature — a live, in-IntelliJ rendering of the widget under development. This can be enabled from the Flutter preferences page in IntelliJ: + +<DashImage figure src="images/0NeOjBxMN6kT43xJ7.webp" /> + + +In order for this to work for a widget, the widget class must define a forDesignTime() constructor. The reason for this is that widgets are usually created with context specific constructor parameters. When rendering the preview of the widget, we don’t necessarily know the right values for these parameters. So, we let the user provide them for us via this specially named constructor. + +We’ve had several comments that Flutter UI code can be quite long and it can be difficult to construct a mental model and navigate while coding. We realized that a visual presentation and two-way synchronization between the code and its visual presentation could help to solve both problems. + +The code for a previewable widget might then look like this: + +```dart +import 'package:flutter/material.dart'; + +class MyWidget extends StatelessWidget { + final String firstText; + + MyWidget(this.firstText); + + factory MyWidget.forDesignTime() { + return new MyWidget('AA'); + } + + @override + Widget build(BuildContext context) { + return new Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: <Widget>[ + buildText(firstText), + buildText('BBBB'), + buildText('CCCCCC'), + buildText('DDDDDDDD'), + ], + ); + } + + Text buildText(String text) { + return new Text(text, style: const TextStyle(fontSize: 24.0)); + } +} +``` + +and its preview would look something like this: + +<DashImage figure src="images/1Zut6fVhPFiAPFp2Uq24H6Q.webp" /> + + +When trying this out, you’ll note that the preview area will show you the layout of any number of different kinds of widgets (constructible using a forDesignTime() constructor), and will update automatically as you type. + +## How does it work? + +The live preview view ties together technology from hot reload, Flutter’s headless testing binary, and Dart’s analysis server, with an implementation in IntelliJ. + +### On file edits + +When you open a Dart file in IntelliJ, the Flutter plugin subscribes to Flutter Outline events from the Dart Analysis Server. Every time a file changes, a new ‘Flutter Outline’ event is sent to IntelliJ with structured information about the contents of a file. This information includes classes, methods, fields, as well as information about the Flutter widgets created in the file. Flutter items are created for every instantiation of a Widget subclass and any reference to a Widget typed variable or a method invocation. + +In order to map runtime widget instances back to the locations in source where they were created, we re-write the user’s source code to assign a unique ID to each created widget. You can see the instrumented code here: + +```dart +import 'package:flutter/material.dart'; + +class MyWidget extends StatelessWidget { + final String firstText; + + MyWidget(this.firstText); + + factory MyWidget.forDesignTime() { + return _registerWidgetInstance(0, new MyWidget('AA')); + } + + @override + Widget build(BuildContext context) { + return _registerWidgetInstance(1, new Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: <Widget>[ + _registerWidgetInstance(2, buildText(firstText)), + _registerWidgetInstance(3, buildText('BBBB')), + _registerWidgetInstance(4, buildText('CCCCCC')), + _registerWidgetInstance(5, buildText('DDDDDDDD')), + ], + )); + } + + Text buildText(String text) { + return _registerWidgetInstance(6, new Text(text, style: const TextStyle(fontSize: 24.0))); + } +} +final flutterDesignerWidgets = <int, Widget>{}; + +T _registerWidgetInstance<T extends Widget>(int id, T widget) { + flutterDesignerWidgets[id] = widget; + return widget; +} +``` + +This instrumented code also comes back as part of the analysis server’s response. + +### Populating the outline view + +This information is used to populate the tree portion of the outline view. As the user changes selection in the file, we request a re-rendering of the widget preview using the currently selected widget. + +### Starting the rendering server + +We use a standard Flutter tool, which allows headless execution of Flutter code — the flutter_tester binary. You can think about it as part of Dart VM with the dart:ui library and [Skia](https://skia.org/) bindings, just like on a mobile device, but on your desktop. We write two files into a temporary directory — the instrumented code itself, and the [rendering server](https://github.com/flutter/flutter-intellij/blob/master/src/io/flutter/preview/render_server_template.txt). The rendering server listens on stdin for the render command, instantiates the widget to render, puts it into a MaterialApp, grabs positioning information for each widget, and returns JSON information back to the client, with the bounds information mapped to corresponding widget identifiers. + +### Hot reloading the instrumented code + +But running a new flutter_tester instance for each cycle would be too slow — it takes about 1000 ms for the Dart VM to start and the JIT to run. So, instead of running a new process each time, we use yet another amazing feature of Flutter and Dart — hot reload. We update the rendering server file to use the new preview area size, the new widget class name to instantiate, and of course write newly instrumented code into the file to render. Then we ask the Dart VM to hot reload the entry point (the rendering server in our case), and it picks up all changes. The whole process takes only about 100 ms, which is 10 times faster than the initial, cold, rendering. + +This works well when you switch between files of the same package, because the set of packages stays the same (and we run flutter_tester with this set). But when you switch to a file in another package, we restart the process. We also terminate the process if rendering does not happen fast enough — currently a cutoff of 2000 ms. So, if there is a compilation or a runtime error, once you fix it, eventually the rendering server process will succeed and you will see rendering. + +### Painting in IntelliJ + +Once the Flutter plugin for IntelliJ gets the outline and corresponding set of bounds for each widget, it paints the widget layout in the preview area using standard Swing components. This preview lets you select a widget outline and see the corresponding entry in the tree view and in the source code. + +## Kicking the tires + +So, go to the preferences panel, turn on live preview, and let us know what you think! We hope it helps you edit and understand your Flutter code and UIs. \ No newline at end of file diff --git a/sites/www/content/blog/flutter-package-ecosystem-update/images/08aAcj-L2pE08dBil.webp b/sites/www/content/blog/flutter-package-ecosystem-update/images/08aAcj-L2pE08dBil.webp new file mode 100644 index 0000000000..da0e3d39a7 Binary files /dev/null and b/sites/www/content/blog/flutter-package-ecosystem-update/images/08aAcj-L2pE08dBil.webp differ diff --git a/sites/www/content/blog/flutter-package-ecosystem-update/images/0DcTQUTlLWP5XY08S.webp b/sites/www/content/blog/flutter-package-ecosystem-update/images/0DcTQUTlLWP5XY08S.webp new file mode 100644 index 0000000000..0c8c2b6e48 Binary files /dev/null and b/sites/www/content/blog/flutter-package-ecosystem-update/images/0DcTQUTlLWP5XY08S.webp differ diff --git a/sites/www/content/blog/flutter-package-ecosystem-update/images/0TBaT0BabySTA5w7A.webp b/sites/www/content/blog/flutter-package-ecosystem-update/images/0TBaT0BabySTA5w7A.webp new file mode 100644 index 0000000000..a9efaa123e Binary files /dev/null and b/sites/www/content/blog/flutter-package-ecosystem-update/images/0TBaT0BabySTA5w7A.webp differ diff --git a/sites/www/content/blog/flutter-package-ecosystem-update/images/0jAThFFycWDnklih_.webp b/sites/www/content/blog/flutter-package-ecosystem-update/images/0jAThFFycWDnklih_.webp new file mode 100644 index 0000000000..f56a65de5e Binary files /dev/null and b/sites/www/content/blog/flutter-package-ecosystem-update/images/0jAThFFycWDnklih_.webp differ diff --git a/sites/www/content/blog/flutter-package-ecosystem-update/images/0lfpUSCU8N0mJf1BH.webp b/sites/www/content/blog/flutter-package-ecosystem-update/images/0lfpUSCU8N0mJf1BH.webp new file mode 100644 index 0000000000..63c66db804 Binary files /dev/null and b/sites/www/content/blog/flutter-package-ecosystem-update/images/0lfpUSCU8N0mJf1BH.webp differ diff --git a/sites/www/content/blog/flutter-package-ecosystem-update/images/0wGtSijeLPe4ZesC5.webp b/sites/www/content/blog/flutter-package-ecosystem-update/images/0wGtSijeLPe4ZesC5.webp new file mode 100644 index 0000000000..cddd9c3b1d Binary files /dev/null and b/sites/www/content/blog/flutter-package-ecosystem-update/images/0wGtSijeLPe4ZesC5.webp differ diff --git a/sites/www/content/blog/flutter-package-ecosystem-update/images/1xL8xSvDmpOJNRi8v-uLmmA.webp b/sites/www/content/blog/flutter-package-ecosystem-update/images/1xL8xSvDmpOJNRi8v-uLmmA.webp new file mode 100644 index 0000000000..facca96914 Binary files /dev/null and b/sites/www/content/blog/flutter-package-ecosystem-update/images/1xL8xSvDmpOJNRi8v-uLmmA.webp differ diff --git a/sites/www/content/blog/flutter-package-ecosystem-update/index.md b/sites/www/content/blog/flutter-package-ecosystem-update/index.md new file mode 100644 index 0000000000..826e7c13b7 --- /dev/null +++ b/sites/www/content/blog/flutter-package-ecosystem-update/index.md @@ -0,0 +1,87 @@ +--- +title: "Flutter Package Ecosystem Update" +description: "New Flutter Favorites, Apple Sign In and bringing prerelease Flutter plugins into production" +publishDate: 2020-05-20 +author: csells +image: images/0jAThFFycWDnklih_.webp +category: announcements +layout: blog +--- + +### New Flutter Favorites, Apple Sign In, and bringing prerelease Flutter plugins into production + +The Flutter team at Google has exactly one job: to unblock developers building beautiful, native applications across multiple platforms. When Flutter was new, the team accomplished that goal by focusing on a few initial customers. This resulted in the production of a portable rendering engine for text and graphics. Next up was to expand its reach, which the team accomplished by appealing to developers with a fast workflow combining the power of the Dart platform, Flutter APIs designed for ease-of-use, and native compilation across Android and iOS. As the customer base grew, so did their needs, motivating the team to leverage Dart’s pub.dev to provide a Flutter package manager and discovery mechanism that connected package authors with app developers. + +This last step is especially important because it enabled the Flutter community to help each other without the Flutter team getting in the way. Nobody has to ask before they publish or use a package on [pub.dev](https://pub.dev/). On the other hand, to foster this kind of ecosystem, it’s important to have a trusted agent that establishes the infrastructure on which modern Flutter apps are built. The Flutter team at Google provides the trusted platform on which the Flutter community can continue to grow and thrive. + +## Package Publishers, Ratings and Favorites + +To help make the Flutter packages ecosystem trustworthy, we expose a number of metrics about each package on pub.dev: + +<DashImage figure src="images/08aAcj-L2pE08dBil.webp" /> + + +This is a screenshot of [the provider package](https://pub.dev/packages/provider), which is at the top when it comes to popularity and quality in the Flutter ecosystem. This screenshot shows off several of the metrics we use to signal quality on pub.dev. The blue shield next to the domain name is the [Verified Publisher](https://dart.dev/tools/pub/verified-publishers) and lets you know that a package was published by someone whose identity has been verified. The thumbs-up icon and the number next to it shows the number of Likes, which lets you know how other members of the community feel about this package. And finally, the logo on the far right marks the package as a Flutter Favorite, which indicates a package that you should consider first when building your apps. As just one comparison, the average likeability of an actively maintained package over the last three months is 12 likes, whereas for a Flutter Favorite, it’s 270 likes. To find the highest quality packages, [the Flutter Favorite measures](https://flutter.dev/docs/development/packages-and-plugins/favorites#metrics) combine the pub.dev score along with a number of objective and subjective measures for quality, usability, and broad applicability, including an engineering code review from one of the contributors to the Flutter framework itself. + +Because of the high bar, the Flutter Favorite designation is something you won’t see very often — it only appears on [a handful of packages](https://pub.dev/flutter/favorites). Today I’m pleased to announce that the list has grown to include the following: [bloc](https://pub.dev/packages/bloc) and [flutter_bloc](https://pub.dev/packages/flutter_bloc), [convex_bottom_bar](https://pub.dev/packages/convex_bottom_bar), [geolocator](https://pub.dev/packages/geolocator), [google_fonts](https://pub.dev/packages/google_fonts), [location](https://pub.dev/packages/location), and [sign_in_with_apple](https://pub.dev/packages/sign_in_with_apple). Congratulations to all of those authors and thank you very much for your contributions to the Flutter package ecosystem. + +## Sign In with Apple + +One of the most notable plugins from this round of Flutter Favorite awards is [the sign_in_with_apple plugin](https://pub.dev/packages/sign_in_with_apple). + +<DashImage figure src="images/0jAThFFycWDnklih_.webp" /> + + +Not only is it of high quality and broad applicability, but it fills an important gap in the Flutter feature set. [Apple has announced](https://developer.apple.com/news/?id=03262020b) that apps that use other third-party authentication, such as Google Sign In, must also enable Apple Sign In by June 30, 2020 to be accepted in the App Store. Obviously this was crucial functionality to provide for any Flutter developer targeting iOS, but instead of the Flutter team at Google building its own plugin to fill this gap, we leaned on our community. Reaching out to Timm Preetz and Henri Beck, the authors of sign_in_with_apple, we found them to be very responsive and quickly brought the plugin up to the required bar in record time. + +## Increasing Quantity and Quality + +It’s only with the maturity and diversity of the Flutter package ecosystem that we were able to work with the community to achieve something like the sign_in_with_apple plugin. If you look at the number of Flutter-dependent packages over time, you can see the growth of the ecosystem. + +<DashImage figure src="images/0wGtSijeLPe4ZesC5.webp" alt="*Number of Flutter-dependent packages since the 1.0 release*" caption="*Number of Flutter-dependent packages since the 1.0 release*" /> + + +Normally, you’d expect such amazing growth to be accompanied by an overall decrease in quality. However, the Flutter community is seeing just the opposite trend. + +<DashImage figure src="images/0TBaT0BabySTA5w7A.webp" alt="*Percentage of high quality Flutter-dependent packages since 1.0 release*" caption="*Percentage of high quality Flutter-dependent packages since 1.0 release*" /> + + +Here we’re measuring quality as the health and maintenance portion of the overall score on pub.dev. As you can see, since the Flutter 1.0 release, the overall quality of the package ecosystem has increased despite the enormous growth. + +## Moving Google Flutter Plugins to 1.0 + +It’s not just the community that produces plugins. A team of Googlers working on Flutter also provide [a set of plugins](https://pub.dev/publishers/flutter.dev/packages). Many of those plugins are marked as Flutter Favorite packages, indicating high quality, including [shared_preferences](https://pub.dev/packages/shared_preferences), [path_provider](https://pub.dev/packages/path_provider), [url_launcher](https://pub.dev/packages/url_launcher) and more. However, those plugins are not all marked as “1.0”; for example, shared_preferences is marked as “0.5.7+2”, which may seem unintuitive when it comes to plugins we consider ready for production use. The reason lies in how Dart handles versions: + +<DashImage figure src="images/0DcTQUTlLWP5XY08S.webp" /> + + +By convention, we advise developers to use the caret syntax when depending on a package, for example: ^current-version. This syntax is interpreted by ‘flutter pub get’ to mean “I’d like the highest available version equal to or greater than the version I’ve specified without changing the major version.” This is [Dart’s implementation](https://medium.com/@nex3/pubgrub-2fb6470504f) of [semantic versioning](https://semver.org/), which states that major versions are to be used for breaking changes and should be chosen explicitly. However, in the case of this set of Flutter plugins maintained by Google, we’ve stabilized the API such that it’s safe to specify the version string to include 1.x versions, which you can do in your pubspec.yaml like so: + +``` +dependencies: + + shared_preferences: ‘>=0.5.7+2 <2.0.0’ +``` + + +For this set of plugins, you can use the following version strings to prepare for v1.0 instead of the conventional versions: + +<DashImage figure src="images/1xL8xSvDmpOJNRi8v-uLmmA.webp" /> + + +Until your pubspec.yaml is updated along these lines, if we were to bump the version numbers to 1.0.0, your apps and dependent packages won’t get the updates. And that’s fine for 1.0, since it’s not going to be different than the 0.x version listed above, but what about 1.1 and beyond? That’s why you want to make these changes. + +In some cases, we can help. For example, there are 181 packages on pub.dev that depend on shared_preferences. If your app depends on shared_preferences <2.0.0 and also depends on another package that in turn depends on shared_preferences <1.0.0, then the Dart version solver is never going to give you 1.0.0 (or 1.1.0 or …). However, instead of waiting and hoping for the authors of these 181 packages to read this blog post and update their dependency version string for all of the plugins listed above, we’re actively working to submit PRs to the git repositories that contain the packages’ pubspec.yaml to match the version strings in the table above. That way, when we flip the version number to 1.0.0 on the set of Google Flutter plugins currently at 0.x, your apps will be all set and the packages that you depend on will be all set, too. + +## Bringing Google Flutter Plugins to Production + +You may have noticed that not all of Google Flutter plugins are listed in the discussion above, like Firebase, Camera, Video Player, etc. That’s because we’ve got some more work to do on some plugins to bring them up to production quality; for example, the Firebase plugins need better, more thorough documentation as well as faster build times and simpler configuration. To help us get there, I’m happy to announce that we’ve engaged with [Invertase](http://invertase.io), a UK-based consulting firm with deep expertise in Dart and Flutter. + +<DashImage figure src="images/0lfpUSCU8N0mJf1BH.webp" /> + + +In the coming months, Invertase is going to help us bring the remaining Google Flutter plugins to production quality. If you’d like more details on the Firebase plugins specifically, you can read about Invertase’s plans on [the FlutterFire repo](https://github.com/FirebaseExtended/flutterfire/issues/2582). + +## Summary + +The Flutter package manager is a thriving ecosystem, with a growing number of packages to fill the gaps that the Googlers on the Flutter team could never possibly manage themselves. Luckily, with the signals on pub.dev, including the score, ratings and the Flutter Favorite designation, as well as our partnership with Invertase, you can have confidence that you’ll have the quality signals you need to find the right packages for your next Flutter app. With all the power provided by the packages on pub.dev, what are you going to build? \ No newline at end of file diff --git a/sites/www/content/blog/flutter-performance-updates-in-2019/images/1VpFwtNIAQAKacPIFLIbGYw.webp b/sites/www/content/blog/flutter-performance-updates-in-2019/images/1VpFwtNIAQAKacPIFLIbGYw.webp new file mode 100644 index 0000000000..bf66120489 Binary files /dev/null and b/sites/www/content/blog/flutter-performance-updates-in-2019/images/1VpFwtNIAQAKacPIFLIbGYw.webp differ diff --git a/sites/www/content/blog/flutter-performance-updates-in-2019/images/1bWXPMmCXDmBWN7rZbxZcyg.webp b/sites/www/content/blog/flutter-performance-updates-in-2019/images/1bWXPMmCXDmBWN7rZbxZcyg.webp new file mode 100644 index 0000000000..104067621a Binary files /dev/null and b/sites/www/content/blog/flutter-performance-updates-in-2019/images/1bWXPMmCXDmBWN7rZbxZcyg.webp differ diff --git a/sites/www/content/blog/flutter-performance-updates-in-2019/index.md b/sites/www/content/blog/flutter-performance-updates-in-2019/index.md new file mode 100644 index 0000000000..6abd6eaeb2 --- /dev/null +++ b/sites/www/content/blog/flutter-performance-updates-in-2019/index.md @@ -0,0 +1,310 @@ +--- +title: "Flutter Performance Updates in 2019" +description: "By Yuqian Li and Shams Zakhour" +publishDate: 2020-07-31 +author: sfshaza2 +image: images/1bWXPMmCXDmBWN7rZbxZcyg.webp +category: announcements +layout: blog +--- + +Being fast is a key pillar to Flutter. This article highlights performance improvements from the second half of 2019 implemented by folks from the Flutter community. (Yes, this is late, but it’s better late than never!) + +If you’ve helped out with performance in 2020, we will cover that in a future post. We hope that sharing this with you, the Flutter community, inspires you to help us with this effort! + +## Q4 (Oct-Dec) 2019 + +### Quantified improvements + +<DashImage figure src="images/1bWXPMmCXDmBWN7rZbxZcyg.webp" alt="70% memory reduction in fast scrolling through large images" caption="70% memory reduction in fast scrolling through large images" /> + + +**70% memory reduction in fast scrolling through large images +contributors: [liyuqian](https://github.com/liyuqian), [dnfield](https://github.com/dnfield), [chinmaygarde](https://github.com/chinmaygarde)** + +* [PR 14265: Cleanup the IO thread GrContext](https://github.com/flutter/engine/pull/14265) + +* [PR 46184: Memory test on scrolling large images quickly](https://github.com/flutter/flutter/pull/46184) + +* [Dashboard: 70% reduction (400MB to 120MB) in fast_scroll_large_images__memory diff-median](https://flutter-dashboard.appspot.com/benchmarks.html) + +* [Issue 19558: IO thread GrContext memory needs to be cleaned up](https://github.com/flutter/flutter/issues/19558) + +**40% reduction in CPU/GPU usages for simple iOS animations +contributors: [flar](https://github.com/flar), [liyuqian](http://github.com/liyuqian), [hixie](https://github.com/hixie), [chinmaygarde](http://github.com/chinmaygarde)** + +* [PR 14104: Rework simpler conditional offscreen for screen readback support](https://github.com/flutter/engine/pull/14104) + +* [PR 13976: Dynamically determine whether to use offscreen surface based on need](https://github.com/flutter/engine/pull/13976) + +* [PR 31865: Simple animation costs high CPU/GPU/Power usages (~20%) on iPhone 6](https://github.com/flutter/flutter/issues/31865) + +* [40% (23%-CPU-14%-GPU to 13%-CPU-8.5%-GPU) reduction in simple_animation_perf_iphonexs cpu_percentage, gpu_percentage](https://github.com/flutter/flutter/issues/31865#issuecomment-566268237) + +**41% speedup for caret performance +contributors: [garyqian](https://github.com/garyqian), [liyuqian](http://github.com/liyuqian), [justinmc](https://github.com/justinmc)** + +* [PR 46720: Pass _caretPrototype to prevent cache miss](https://github.com/flutter/flutter/pull/46720) + +* [PR 46720: 41% speedup (6.709ms to 4.756ms) for 90th percentile frame build time](https://github.com/flutter/flutter/pull/46720) + +* Fixed [Issue 24522: Caret performance is poor, high GPU time per frame](https://github.com/flutter/flutter/issues/24522) + +**10% speedup for list scrolling by fixing raster cache throttling +contributors: [liyuqian](http://github.com/liyuqian), [chinmaygarde](http://github.com/chinmaygarde), [flar](https://github.com/flar), [cyanglaz](https://github.com/cyanglaz), [zsunkun](https://github.com/zsunkun)** + +* [PR 31865: Simple animation costs high CPU/GPU/Power usages (~20%) on iPhone 6](https://github.com/flutter/flutter/issues/31865) + +* [PR 13710: Fix picture raster cache throttling](https://github.com/flutter/engine/pull/13710) + +* [PR 45050: Add a perf test for picture raster cache](https://github.com/flutter/flutter/pull/45050) + +* Fixed a blocker issue, [43083: List scrolling is not smooth](https://github.com/flutter/flutter/issues/43083) with [PR 13710: Fix picture raster cache throttling](https://github.com/flutter/flutter/issues/13710) + +**37x speedup for cached benchmarks load time ([Dashboard](https://flutter-dashboard.appspot.com/benchmarks.html)) +contributors: [caseyhillers](https://github.com/caseyhillers), [tvolkert](https://github.com/tvolkert), [digiter](https://github.com/digiter), [jonahwilliams](https://github.com/jonahwilliams)** + +* [PR 494: Cache get-benchmarks](https://github.com/flutter/cocoon/pull/494) + +* [PR 484: /api/public/get-status serves cached responses](https://github.com/flutter/cocoon/pull/484) + +* [37x speedup (37s to 1s) in benchmarks loading time](https://github.com/flutter/cocoon/pull/494#issue-333227543) + +**2.3x speedup for building APKs +contributors: [jonahwilliams](https://github.com/jonahwilliams), [blasten](https://github.com/blasten), [zanderso](https://github.com/zanderso), [xster](https://github.com/xster)** + +* [PR 44534: Improve performance of build APK (~50%) by running gen_snapshot concurrently](https://github.com/flutter/flutter/pull/44534) + +* [2.3x speedup (140s to 60s) in a release build of an APK for target platforms android-arm, android-arm64, and android-x64](https://github.com/flutter/flutter/pull/44534#issue-339112036) + +**+103 performance metrics recorded per Flutter engine commit +contributors: [liyuqian](http://github.com/liyuqian), [digiter](https://github.com/digiter), [keyonghan](https://github.com/keyonghan), [godofredoc](https://github.com/godofredoc), [cbracken](https://github.com/cbracken)** + +* [PR 14556: Run and collect benchmarks](https://github.com/flutter/engine/pull/14556) + +* Related issue: [PR 37434: Centralize performance metrics, provide universal alerts, and improve performance dashboard](https://github.com/flutter/flutter/issues/37434) + +**20% app size reduction +contributors: [mraleph](https://github.com/mraleph), [alexmarkov](https://github.com/alexmarkov), [rmacnak-google](https://github.com/rmacnak-google), [mkustermann](https://github.com/mkustermann), [sstrickl](https://github.com/sstrickl), [aartbik](https://github.com/aartbik)** + +* 3% from [Lift PC offsets out of StackMaps](https://github.com/dart-lang/sdk/commit/a2bb7301c5795e6b28089a8dc96e6ab5ca798e22) + +* 2.58% from [Further compress the information previously in StackMaps](https://github.com/dart-lang/sdk/commit/d77f4913a18ecce8c4be95cbaa4299ff1521dc10) + +* 1% from [Canonicalize CompressedStackMaps payloads when possible](https://github.com/dart-lang/sdk/commit/dc808f3fcbf7e6de7e2b25441ff7ed891362e70a) + +* 2% from [Fully enable deduplication of instructions in bare mode](https://github.com/dart-lang/sdk/commit/e2faac751e1ef3707730e6e48f4d8f22ecbf35c3) + +* 0.3% from [Do not generate monomorphic prologue for functions which need args descriptor](https://github.com/dart-lang/sdk/commit/f6477854cdb0da052be3423a24961feaf5a0d845) + +* 1% from [Drop redundant initializing stores of null](https://github.com/dart-lang/sdk/commit/46cef9bfddf3cfc05618448d228f0e3377058baf) + +* 6% from [Reduce alignment of Instructions and remove some debugging trap instructions](https://github.com/dart-lang/sdk/commit/8e7ffafbafc8203361111ddcafe0e0fcc372edf8) + +* 1.2% from [Adjust CSP during the invocation stub instead of each function prologue](https://github.com/dart-lang/sdk/commit/c873220e43af1a1f4675df4108e575465e598578) + +* 1% from [ARM64: Block R22 to hold NullObject()](https://github.com/dart-lang/sdk/commit/f8d42542ddd40c06bb71d6fb85d78ed4bbffa785) + +* 2.5% from [Whole-program constant propagation](https://github.com/dart-lang/sdk/commit/f56b0f690789b6f0e2e5bc1340abf4eba414b7a0) + +* 0.77% from [Dead code elimination](https://github.com/dart-lang/sdk/commit/b69596bb1ba81874e4b9d7a577071c67c357ec39) + +**108x speedup on Dart FFI performance +contributors: [dcharkes](https://github.com/dcharkes), [mkustermann](https://github.com/mkustermann), [sjindel](https://github.com/sjindel), [alexmarkov](https://github.com/alexmarkov)** + +* [Gerrit 120661: Optimize Pointer operations for statically known types](https://dart-review.googlesource.com/c/sdk/+/120661) + +* [Gerrit 119645: Pointer optimize indexed load and store](https://dart-review.googlesource.com/c/sdk/+/119645) + +* [Gerrit 121580: Allow inlining of force optimized functions in AoT](https://dart-review.googlesource.com/c/sdk/+/121580) + +**10–15% performance improvement in tight code +contributors: [aartbik](https://github.com/aartbik), [mkustermann](https://github.com/mkustermann), [mraleph](https://github.com/mraleph)** + +* [Gerrit 117200: Loop analysis and BCE improvements](https://dart-review.googlesource.com/c/sdk/+/117200) + +* 10–15% performance improvement in golem armv7, and TypedData Bench: [Gerrit 117200: Loop analysis and BCE improvements](https://dart-review.googlesource.com/c/sdk/+/117200) + +**2.2x speedup in flutter test with new incremental serializer +contributors: [jensjoha](https://github.com/jensjoha), [alexmarkov](https://github.com/alexmarkov)** + +* [Gerrit 121121: Enable incremental serializer by default](https://dart-review.googlesource.com/c/sdk/+/121121) + +* 2.2x speedup (3:38 to 1:39) in `flutter test` + +**10% faster Kernel binary serialization by giving inlining hints to Dart VM JIT +contributors: [jensjoha](https://github.com/jensjoha), [johnniwinther](https://github.com/johnniwinther)** + +* [Gerrit 119540: Add VM pragma prefer-inline to certain methods in kernel serialization](https://dart-review.googlesource.com/c/sdk/+/119540) + +**30% performance improvement on async heavy code +contributors: [cskau-g](https://github.com/cskau-g), [mkustermann](https://github.com/mkustermann), [mraleph](https://github.com/mraleph)** + +* [Adds non-eager async stack frame collector](https://dart-review.googlesource.com/c/sdk/+/122644) + +## Other improvements + +**Fixed a memory leak when using `PlatformView` on iOS** + +* [Fix Memory leak when using `PlatformView` [IOS]](https://github.com/flutter/engine/pull/7919) + +* [Memory leak when using `PlatformView` [IOS]](https://github.com/flutter/flutter/issues/24714) + +## **Fixed a memory leak when animation is playing on iOS** + +* [Gerrit 260538: Don’t allocate invalidation messages for generators that make uncacheable textures](https://skia-review.googlesource.com/c/skia/+/260538) + +* [Memory Leak when animation is playing in iOS](https://github.com/flutter/flutter/issues/47108) + +**Fixed more iOS memory leaks** + +* [https://github.com/flutter/engine/pull/14275](https://github.com/flutter/engine/pull/14275) + +* [https://github.com/flutter/engine/pull/14326](https://github.com/flutter/engine/pull/14326) + +* [https://github.com/flutter/flutter/issues/35243](https://github.com/flutter/flutter/issues/35243) + +**Started revamping [Performance](https://flutter.dev/docs/perf) pages on flutter.dev and added instructions on [measuring app size](https://flutter.dev/docs/perf/app-size).** + +* [PR 3159: Performance](https://github.com/flutter/website/pull/3159/) + +**Corrected the first frame waiting logic and measurement** + +* [PR 37192: Reland “Fix the first frame logic in tracing and driver (#35297)”](https://github.com/flutter/flutter/pull/37192) + +* Fixed [Issue 47108: Memory Leak when animation is playing in iOS](https://github.com/flutter/flutter/issues/47108) + +**DevTools added full timeline mode with support for async and recorded tracing.** + +* [PR 1241: Add full timeline view](https://github.com/flutter/devtools/pull/1241) + +**IntelliJ plugin fixed 120FPS support** + +* [PR 4289: Remove hard coded refresh rate for fps calculation](https://github.com/flutter/flutter-intellij/pull/4289) + +**Many timeline tracing improvements thanks to ByteDance** + +* [Gerrit 127920: [timeline] Add support for timeline asynchronous events in android platform trace](https://dart-review.googlesource.com/c/sdk/+/127920) + +* [Gerrit 128200: [timeline] support vm events available to systrace](https://dart-review.googlesource.com/c/sdk/+/128200) + +* [Gerrit 127921: support more sync event when use systrace to record timeline event](https://dart-review.googlesource.com/c/sdk/+/127921) + +* [PR 14323: Fix missing API stream when record event in systrace](https://github.com/flutter/engine/pull/14323) + +* [PR 14521: Support timeline can be enabled in release mode](https://github.com/flutter/engine/pull/14521) + +* [PR 14319: Fix missing timeline event of flutter engine’s startup time](https://github.com/flutter/engine/pull/14319) + +* [PR 47742: fix duration event of timeline summary](https://github.com/flutter/flutter/pull/47742) + +* [Gerrit 131360: Support timeline conversion to iOS platform trace](https://dart-review.googlesource.com/c/sdk/+/131360) + +* [PR 16520: support endless trace buffer](https://github.com/flutter/engine/pull/16520) + +* [PR 47419: support endless recorder for timeline](https://github.com/flutter/flutter/pull/47419) + +## Q3 (July-Sept) 2019 + +### Quantified improvements + +**1.5–5x speedup for rect & point transformations +contributors: [flar](https://github.com/flar), [yjbanov](https://github.com/yjbanov), [dnfield](https://github.com/dnfield)** + +* [PR 37275: Optimize the transformRect and transformPoint methods in matrix_utils](https://github.com/flutter/flutter/pull/37275) + +* 5.3x speedup (2300ms to 430ms) in `MatrixUtils_affine_transformRect_iteratio[n](http://go/flutter-benchmarks)` + +* 1.5x speedup (466ms to 320ms) in `MatrixUtils_affine_transformPoint_iteration` + +**N/2–1 fewer missed frames on iPhone X/Xs scrolling +contributors: [liyuqian](http://github.com/liyuqian), [chinmaygarde](http://github.com/chinmaygarde), [gaaclarke](https://github.com/gaaclarke)** + +* [https://github.com/flutter/engine/pull/12385](https://github.com/flutter/engine/pull/12385) + +* [PR 12385: Reland “Smooth out iOS irregular input events delivery](https://github.com/flutter/engine/pull/12385) + +* Reduced the worst-case number of missed frames from N/2 to 1 in a scroll of N frames. In practice, N/10 frames were missed on average before the fix. + +* Fixed one of our top-voted issues: +[Issue 31086: Scroll performance significantly degraded on iPhone X, Xs devices due to irregular input events delivery](https://github.com/flutter/flutter/issues/31086) + +**15% faster engine start and shutdown with parallel initialization +contributors: [gaaclarke](https://github.com/gaaclarke), [chinmaygarde](http://github.com/chinmaygarde), [liyuqian](http://github.com/liyuqian)** + +* [PR 10182: Made flutter startup faster by allowing initialization to be parallelized](https://github.com/flutter/engine/pull/10182) + +* [1.16x speedup (3829377 ns to 3286713 ns) in BM_ShellInitializationAndShutdown](https://github.com/flutter/engine/pull/10182#issuecomment-531953100) + +**14.57ms faster startup and 8MB smaller memory usage for shader warm-up +contributors: [gaaclarke](https://github.com/gaaclarke), [liyuqian](http://github.com/liyuqian), [dnfield](https://github.com/dnfield)** + +* [PR 36482: Sped up shader warmup by only drawing on a 100x100 surface](https://github.com/flutter/flutter/pull/36482) + +* [Saves 14.57ms (18.848ms to 4.279ms) in reading/converting pixels at startup](https://github.com/flutter/flutter/pull/36482#issue-299068276) + +* [Saves 8MB (39220KB to 31184KB) in start-median memory](https://github.com/flutter/flutter/pull/36482#issuecomment-531067365) + +* [Saves 4MB (45034KB to 40980KB) in end-median memory](https://github.com/flutter/flutter/pull/36482#issuecomment-531067365) + +**1.02%-8.04% reduction in code size +contributors: [johnniwinther](https://github.com/johnniwinther), [aartbik](https://github.com/aartbik), [rmacnak-google](https://github.com/rmacnak-google), [jensjoha](https://github.com/jensjoha), [alexmarkov](https://github.com/alexmarkov), [mkustermann](https://github.com/mkustermann)** + +* [https://dart-review.googlesource.com/c/sdk/+/118280](https://dart-review.googlesource.com/c/sdk/+/118280) + +* [https://dart-review.googlesource.com/c/sdk/+/112758](https://dart-review.googlesource.com/c/sdk/+/112758) + +* [https://dart-review.googlesource.com/c/sdk/+/118181](https://dart-review.googlesource.com/c/sdk/+/118181) + +* -8.04% (5.57MB to 5.13MB) in armv8 animation_bench_instructions_size + +* -2.7% (2.10MB to 2.05MB) in armv7 flutter_gallery_readonlydata_size + +* -1.22% (2.10MB to 2.05MB) in armv7 layout_bench_instructions_size + +**Up-to-2x increase for Flutter on Fuchsia FPS; improved frame scheduling +contributors: [dreveman](https://github.com/dreveman), [amott](https://github.com/amott), [rosswang](https://github.com/rosswang), [mikejurka](https://github.com/mikejurka)** + +* [https://fuchsia-review.googlesource.com/c/topaz/+/280230](https://fuchsia-review.googlesource.com/c/topaz/+/280230) + +* [https://fuchsia-review.googlesource.com/c/topaz/+/286735](https://fuchsia-review.googlesource.com/c/topaz/+/286735) + +* [https://fuchsia-review.googlesource.com/c/topaz/+/300135](https://fuchsia-review.googlesource.com/c/topaz/+/300135) + +* [https://fuchsia-review.googlesource.com/c/topaz/+/306773](https://fuchsia-review.googlesource.com/c/topaz/+/306773) + +* [https://fuchsia-review.googlesource.com/c/topaz/+/306772](https://fuchsia-review.googlesource.com/c/topaz/+/306772) + +* [https://fuchsia-review.googlesource.com/c/topaz/+/307953](https://fuchsia-review.googlesource.com/c/topaz/+/307953) + +### Quantified regression fixes + +**3x speedup for BackdropFilter on iOS +contributors: [lhkbob](https://github.com/lhkbob), [liyuqian](http://github.com/liyuqian), [flar](https://github.com/flar)** + +* [https://skia-review.googlesource.com/c/skia/+/237904](https://skia-review.googlesource.com/c/skia/+/237904) + +* [https://skia-review.googlesource.com/c/skia/+/234413](https://skia-review.googlesource.com/c/skia/+/234413) + +* [https://github.com/flutter/flutter/pull/38814](https://github.com/flutter/flutter/pull/38814) + +* [3x speedup (110ms to 34ms) in GM_savelayer_with_backdrop](https://perf.skia.org/e/?begin=1567010155&end=1567104574&queries=name%3DGM_savelayer_with_backdrop%26os%3DiOS) + +* Fixed regression [https://github.com/flutter/flutter/issues/36064](https://github.com/flutter/flutter/issues/36064) + +To achieve some huge improvements (3x for example), the bad performance of the old state probably played an equally important role as the hard work in Q3 (July-Sept) 2019. We also marked some improvements as non-trivial fixes of equally big regressions. Nevertheless, we appreciate such work. Without it, we’d continue to have bad performance and regressions. We don’t want the big improvements to dwarf the smaller improvements. They just didn’t have a very bad old performance to start with, which in some sense is a good thing. + +### Other improvements + +* DevTools [support variable display refresh rates (e.g. 60 FPS, 120 FPS, etc.)](https://github.com/flutter/devtools/blob/master/packages/devtools/CHANGELOG.md#018---2019-10-01) + +* VSCode plugin’s scanning for projects is now asynchronous which should improve extension activation speed and reduce the chances of triggering VS Code’s “extension causes high CPU” warning. ([#1840](https://github.com/Dart-Code/Dart-Code/issues/1840)/[#2003](https://github.com/Dart-Code/Dart-Code/issues/2003)/[#1961](https://github.com/Dart-Code/Dart-Code/issues/1961)) + +* iPhone Xs is [added to Flutter device lab](https://github.com/flutter/flutter/issues/34590) for benchmarking + +## Conclusion + +<DashImage figure src="images/1VpFwtNIAQAKacPIFLIbGYw.webp" alt="57% of respondents are very satisfied with Flutter. In total, 94% are positively satisfied." caption="57% of respondents are very satisfied with Flutter. In total, 94% are positively satisfied." /> + + +Thanks to these contributions from our community, the proportion of users positively satisfied with Flutter’s mobile performance increased from 85% in Q3 2019 to 92% in 2020. Despite our best effort, some performance contributions in Q3-Q4 2019 may be missed in this update. Please don’t hesitate to let us know of any missing contributions, and we’ll put them in the next update. \ No newline at end of file diff --git a/sites/www/content/blog/flutter-performance-updates-in-the-first-half-of-2020/images/15WEbz38iKKh5EfCj55mGYw.webp b/sites/www/content/blog/flutter-performance-updates-in-the-first-half-of-2020/images/15WEbz38iKKh5EfCj55mGYw.webp new file mode 100644 index 0000000000..f19609f9fb Binary files /dev/null and b/sites/www/content/blog/flutter-performance-updates-in-the-first-half-of-2020/images/15WEbz38iKKh5EfCj55mGYw.webp differ diff --git a/sites/www/content/blog/flutter-performance-updates-in-the-first-half-of-2020/index.md b/sites/www/content/blog/flutter-performance-updates-in-the-first-half-of-2020/index.md new file mode 100644 index 0000000000..9ea73ce7c9 --- /dev/null +++ b/sites/www/content/blog/flutter-performance-updates-in-the-first-half-of-2020/index.md @@ -0,0 +1,408 @@ +--- +title: "Flutter Performance Updates in the first half of 2020" +description: "By Yuqian Li and Shams Zakhour" +publishDate: 2021-02-12 +author: liyuqian +image: images/15WEbz38iKKh5EfCj55mGYw.webp +category: announcements +layout: blog +--- + +## Flutter performance updates in the first half of 2020 + +Being fast is a key pillar to Flutter. This article highlights performance improvements from the first half of 2020 implemented by folks from the Flutter community. + +If you’ve helped out with performance after June 2020, we will cover that in a future post. We hope that sharing this with you, the Flutter community, inspires you to help us with this effort! + +## Quantified improvements + +### [app size] 18.5% reduction in Flutter Gallery app size + +contributors: [alexmarkov](https://github.com/alexmarkov), [sstrickl](https://github.com/sstrickl), [mkustermann](https://github.com/mkustermann), [askeksa-google](https://github.com/askeksa-google), [rmacnak-google](https://github.com/rmacnak-google), [mraleph](https://github.com/mraleph), [crelier](https://github.com/crelier) + +* Commit [f56b0f](https://github.com/dart-lang/sdk/commit/f56b0f690789b6f0e2e5bc1340abf4eba414b7a0), [a2bb73](https://github.com/dart-lang/sdk/commit/a2bb7301c5795e6b28089a8dc96e6ab5ca798e22), [d77f49](https://github.com/dart-lang/sdk/commit/d77f4913a18ecce8c4be95cbaa4299ff1521dc10), [e2faac](https://github.com/dart-lang/sdk/commit/e2faac751e1ef3707730e6e48f4d8f22ecbf35c3), [8e7ffa](https://github.com/dart-lang/sdk/commit/8e7ffafbafc8203361111ddcafe0e0fcc372edf8), [76c628](https://dart-review.googlesource.com/c/sdk/+/126648), [4ab9c7](https://dart-review.googlesource.com/c/sdk/+/131833), [1c2d37](https://dart-review.googlesource.com/c/sdk/+/132603), [27e64c](https://dart-review.googlesource.com/c/sdk/+/132663), [6a7e4e](https://dart-review.googlesource.com/c/sdk/+/134294), [054929](https://dart-review.googlesource.com/c/sdk/+/152323) + +* 18.5% reduction ([9.6MB to 8.1MB](https://golem.corp.goog/Benchmark?team=flutter&benchmark=flutter_gallery_total_size)) the Flutter Gallery sample for Android + +### [app size] 100KB size reduction for Gallery app by font subsetting + +contributors: [dnfield](https://github.com/dnfield), [jonahwilliams](https://github.com/jonahwilliams), [zanderso](https://github.com/zanderso), [jmagman](https://github.com/jmagman), [blasten](https://github.com/blasten) + +* PR [#14828](https://github.com/flutter/engine/pull/14828), [#49737](https://github.com/flutter/flutter/pull/49737), [#49842](https://github.com/flutter/flutter/pull/49842), [#50224](https://github.com/flutter/flutter/pull/50224), [#51808](https://github.com/flutter/flutter/pull/51808) + +* 100kb [size reduction](https://github.com/flutter/flutter/pull/49737#issue-368790736) in Flutter Gallery app + +* Fixed “Tree shake material icons [#16311](https://github.com/flutter/flutter/issues/16311)”, “Build hb-subset as part of engine builds and upload to cloud storage [#43642](https://github.com/flutter/flutter/issues/43642)”, “Consume font-subset in flutter_tools and treeshake icon fonts [#43644](https://github.com/flutter/flutter/issues/43644)”, “Wire up font subsetting for all platforms [#49730](https://github.com/flutter/flutter/issues/49730)” + +### [speed, memory] 9x speedup and 99+% memory reduction on dart2js tools + +contributors: [rmacnak-google](https://github.com/rmacnak-google), [a-siva](https://github.com/a-siva) + +* Commit [5b8c4e](https://dart-review.googlesource.com/c/sdk/+/130621) + +* [9x speedup (46 to 5s) and 99+% memory reduction (537 to 1.5MB)](https://dart-review.googlesource.com/c/sdk/+/130621) on dart2js self compile, 1.5GB heap, 400MB heap snapshot + +### [speed] Up to 50% speedup for iOS average render speed by moving from OpenGL to Metal + +contributors: [chinmaygarde](https://github.com/chinmaygarde), [dnfield](https://github.com/dnfield), [jason-simmons](https://github.com/jason-simmons), [cbracken](https://github.com/cbracken), [amirh](https://github.com/amirh), [liyuqian](https://github.com/liyuqian) + +* PR [#8936](https://github.com/flutter/engine/pull/8936), [#9383](https://github.com/flutter/engine/pull/9383), [#11070](https://github.com/flutter/engine/pull/11070), [#13056](https://github.com/flutter/engine/pull/13056), [#13367](https://github.com/flutter/engine/pull/13367), [#17080](https://github.com/flutter/engine/pull/17080), [#17162](https://github.com/flutter/engine/pull/17162), [#17154](https://github.com/flutter/engine/pull/17154), [#17431](https://github.com/flutter/engine/pull/17431) + +* 50% speedup ([6ms to 4ms](https://github.com/flutter/flutter/issues/53768)) for flutter_gallery_ios__transition_perf average_frame_rasterizer_time_millis + +* Fixed “Flutter should use Metal instead of OpenGL on iOS [#18208](https://github.com/flutter/flutter/issues/18208)” + +### [speed] 2–5x speedup for the worst frame raster time caused by shader compilations + +contributors: [liyuqian](https://github.com/liyuqian), [jonahwilliams](https://github.com/jonahwilliams), [chinmaygarde](https://github.com/chinmaygarde), [iskakaushik](https://github.com/iskakaushik), [zanderso](https://github.com/zanderso), [sfshaza2](https://github.com/sfshaza2), [filiph](https://github.com/filiph), [acoutts](https://github.com/acoutts) + +<DashImage figure src="images/15WEbz38iKKh5EfCj55mGYw.webp" /> + + +* PR [#17300](https://github.com/flutter/engine/pull/17300), [#53859](https://github.com/flutter/flutter/pull/53859), [#17601](https://github.com/flutter/engine/pull/17601), [#17861](https://github.com/flutter/engine/pull/17861), [#58743](https://github.com/flutter/flutter/pull/58743), [#56638](https://github.com/flutter/flutter/pull/56638), [#4430](https://github.com/flutter/website/pull/4430), [#12142](https://github.com/flutter/engine/pull/12412) + +* 2.25x speedup ([90ms to 40ms](https://flutter-flutter-perf.skia.org/e/?begin=1597812484&end=1597873497&queries=branch%3Dmaster%26sub_result%3Dworst_frame_rasterizer_time_millis%26test%3Dflutter_gallery_sksl_warmup__transition_perf%26test%3Dflutter_gallery__transition_perf)) for Android flutter_gallery__transition_perf worst_frame_rasterizer_time_millis + +* 5x speedup ([300ms to 60ms](https://flutter-flutter-perf.skia.org/e/?begin=1597686064&end=1597961767&numCommits=190&queries=branch%3Dmaster%26sub_result%3Dworst_frame_rasterizer_time_millis%26test%3Dflutter_gallery_sksl_warmup_ios32__transition_perf%26test%3Dflutter_gallery_ios32__transition_perf&showZero=false)) for iOS flutter_gallery_ios32__transition_perf worst_frame_rasterizer_time_millis + +* 3.75x speedup ([229ms to 61ms](https://github.com/flutter/flutter/issues/61045#issuecomment-668609516)) for some real apps. + +* Fixed “SkSL-based shader warmup [#53607](https://github.com/flutter/flutter/issues/53607)”, “cubic bezier regression [#35142](https://github.com/flutter/flutter/issues/35142)”, and “[saved](https://github.com/flutter/flutter/issues/61045#issuecomment-673409491)” some customers. + +### [speed] 15.8x speedup for mouse hit testing + +contributors: [dkwingsmt](https://github.com/dkwingsmt), [yjbanov](https://github.com/yjbanov), [goderbauer](https://github.com/goderbauer), [gspencergoog](https://github.com/gspencergoog) + +* PR [#59803](https://github.com/flutter/flutter/pull/59803) [#59883](https://github.com/flutter/flutter/pull/59883) + +* 15.8x ([79ms to 5ms](https://github.com/flutter/flutter/pull/59883#issue-437275465)) speedup for bench_mouse_region_grid_scroll frame duration (web). + +* Fixed “Poor performance when subscribing to MouseRegion events [#41194](https://github.com/flutter/flutter/issues/41194)” + +### [speed] 13.9x speedup for platform message response handling + +contributors: [zljj0818](https://github.com/zljj0818), [jason-simmons](https://github.com/jason-simmons), [liyuqian](https://github.com/liyuqian) + +* PR [#18838](https://github.com/flutter/engine/pull/18838), [#18945](https://github.com/flutter/engine/pull/18945), both of which are inspired by [#18808](https://github.com/flutter/engine/pull/18808) + +* Fixed “Image.asset will block UI thread when load large image [#58572](https://github.com/flutter/flutter/issues/58572)” + +* 13.9x ([9164us to 660us](https://flutter-engine-perf.skia.org/e/?begin=1593164913&end=1594241999&num_commits=290&queries=test%3DBM_PlatformMessageResponseDartComplete)) speedup in PlatformMessageResponseDartComplete + +### [speed] 20%-37% speedup for frame build time with opaque routes + +contributors: [goderbauer](https://github.com/goderbauer), [dnfield](https://github.com/dnfield) + +* PR [#48900](https://github.com/flutter/flutter/pull/48900) + +* 20% ([7.38ms to 6.13ms](https://github.com/flutter/flutter/pull/48900#issuecomment-577297920)) speedup in flutter_gallery_ios32__transition_perf average_frame_build_time_millis + +* 37% ([50.45ms to 36.63ms](https://github.com/flutter/flutter/pull/48900#issuecomment-577297920)) speedup in flutter_gallery_ios32__transition_perf 99th_percentile_frame_build_time_millis + +* Fixed “Optimize unneeded rebuild when pushing overlay/navigator opaque content [#45797](https://github.com/flutter/flutter/issues/45797)” + +### [speed] 4.8x speedup for blur using ImageFiltered instead of BackdropFilter + +contributors: [flar](https://github.com/flar), [hixie](https://github.com/hixie), [yjbanov](https://github.com/yjbanov), [liyuqian](https://github.com/liyuqian) + +* PR [#14491](https://github.com/flutter/engine/pull/14491), [#47489](https://github.com/flutter/flutter/pull/47489), [#58277](https://github.com/flutter/flutter/pull/58277), + +* 4.8x speedup ([29ms/frame to 6ms/frame](https://gist.github.com/flar/1cafb43533652e8e16ea224d93c8a1ec)) for a demo app on Moto G4. + +* Fixed “ImageFilter widget [#13489](https://github.com/flutter/flutter/issues/13489)” + +### [speed] Up to 14x speedup when scrolling large static content with Flutter web + +contributors: [yjbanov](https://github.com/yjbanov), [ferhatb](https://github.com/ferhatb) + +* PR [#17621](https://github.com/flutter/engine/pull/17621) + +* 14.01x speedup ([463.74ms to 33.10ms](https://docs.google.com/spreadsheets/d/10AXb-4ulJipM6ZtiieFEBXhmybB6-_gTV6HDQlgcQdA/edit#gid=2047450737&range=D85)) for text_canvas_cached_layout.html.layout.average + +* Fixed “Jank when scrolling large static content with Flutter Web [#42987](https://github.com/flutter/flutter/issues/42987)” and “Drawing text outside canvas’ bounds is expensive on the Web [#48516](https://github.com/flutter/flutter/issues/48516)” + +### [speed] 6.8x speedup for Flutter engine shell initialization + +contributors: [scutlight](https://github.com/scutlight), [jason-simmons](https://github.com/jason-simmons), [gaaclarke](https://github.com/gaaclarke), [liyuqian](https://github.com/liyuqian) + +* PR [#18225](https://github.com/flutter/engine/pull/18225) + +* 6.8x speedup ([18.3ms to 2.7ms](https://flutter-engine-perf.skia.org/e/?begin=1590623523&end=1590741783&keys=X0fffad7016a0440022353623dc52ae28&xbaroffset=17822)) for BM_ShellInitialization + +### [speed] 5x speedup by skipping calling builder when constraints are the same + +contributors: [yjbanov](https://github.com/yjbanov), [hixie](https://github.com/hixie) + +* PR [#55414](https://github.com/flutter/flutter/pull/55414) + +* 5x speedup (~10FPS to ~50FPS) on scrolling the contacts list with the Flokk customer app. + +* Fixed “LayoutBuilder should cache constraints and not rebuild if unchanged [#6469](https://github.com/flutter/flutter/issues/6469)” + +### [speed] 2x speedup for shadows and animation on web + +contributors: [ferhatb](https://github.com/ferhatb), [mdebbar](https://github.com/mdebbar), [yjbanov](https://github.com/yjbanov) + +* PR [#17864](https://github.com/flutter/engine/pull/17864), [#17866](https://github.com/flutter/engine/pull/17866), [#17867](https://github.com/flutter/engine/pull/17867) + +* 2x speedup ([30fps](https://github.com/flutter/flutter/issues/54507#issue-598145549) to [60fps](https://github.com/flutter/flutter/issues/54507#issuecomment-628235938)) for a [sample web app](https://dev.gskinner.com/flutter_perf/shadows/#/). + +* Fixes “Web: Poor performance when using Shadows + Animation [#54507](https://github.com/flutter/flutter/issues/54507)” + +### [speed] 3X speedup in web Flutter Gallery start-up + +contributors: [ferhatb](https://github.com/ferhatb), [clocksmith](https://github.com/clocksmith), [rami-a](https://github.com/rami-a) + +* PR [#109](https://github.com/flutter/gallery/pull/109), [#183](https://github.com/flutter/gallery/pull/183) + +* 3X speedup ([6077ms to 2039ms](https://github.com/flutter/gallery/pull/109#issue-406182087)) in web Flutter Gallery start-up + +### [speed] 2.8x speedup for average frame rasterizer time by enabling raster cache in Fuchsia + +contributors: [dreveman](https://github.com/dreveman), [liyuqian](https://github.com/liyuqian), [chinmaygarde](https://github.com/chinmaygarde), [arbreng](https://github.com/arbreng) + +* PR [#17753](https://github.com/flutter/engine/pull/17753) + +* 2.8x speedup (~10ms to ~3.5ms) for frame_rasterizer_times_avg + +* Fixed “Poor performance on Fuchsia due to raster cache being disabled [#54950](https://github.com/flutter/flutter/issues/54950)” + +### [speed] 1.85x speedup in 90th percentile frame build time by excluding invisible animated images + +contributors: [dnfield](https://github.com/dnfield), [liyuqian](https://github.com/liyuqian), [tvolkert](https://github.com/tvolkert), [digiter](https://github.com/digiter) + +* PR [#50851](https://github.com/flutter/flutter/pull/50851), [#50842](https://github.com/flutter/flutter/pull/50842) + +* 1.85x speedup ([3.148 ms to 1.699 ms](https://github.com/flutter/flutter/pull/50851#issuecomment-586625240)) for animated_placeholder_perf 90th percentile frame build time + +* Fixed “flutter gives low frame rates when long lists with list builder inside cards giving(avg 20–35fps) #[35592](https://github.com/flutter/flutter/issues/35592)” + +### [speed] 2x speedup for Google smart displays P10 FPS + +contributors: [chinmaygarde](https://github.com/chinmaygarde), [dnicoara](https://github.com/dnicoara), [cbracken](https://github.com/cbracken), [jason-simmons](https://github.com/jason-simmons), [asakhart](https://github.com/asakhart), [rdaum](https://github.com/rdaum) + +* PR [#15980](https://github.com/flutter/engine/pull/15980) + +* 2x speedup (30fps to 60fps) by giving embedders a way to tune thread priority. + +* Fixed issue [49551](https://github.com/flutter/flutter/issues/49551) + +### [speed] 2.45x speedup for ImageFilterLayer by enhancing its child cache + +contributors: [flar](https://github.com/flar), [liyuqian](https://github.com/liyuqian) + +* PR [#17175](https://github.com/flutter/engine/pull/17175), [#54903](https://github.com/flutter/flutter/pull/54903), [#58277](https://github.com/flutter/flutter/pull/58277) + +* 2.45x speedup ([18.41 ms to 7.53 ms](https://github.com/flutter/engine/pull/17175#issuecomment-647778369)) for ImageFiltered Transform average_frame_rasterizer_time_millis + +* Also fixed “Transition performance regressed slightly due to OpacityLayer raster cache miss [#52864](https://github.com/flutter/flutter/issues/52864)” + +### [speed] 15% speedup for very large dart2js compilation + +contributors: [rmacnak-google](https://github.com/rmacnak-google), [a-siva](https://github.com/a-siva) + +* Commit [d67fbe](https://dart-review.googlesource.com/c/sdk/+/133660) + +* 1.15x speedup ([545 to 472 seconds](https://dart-review.googlesource.com/c/sdk/+/133660)) very large dart2js compile, 9% reduction ([13.057 to 11.861 GB](https://dart-review.googlesource.com/c/sdk/+/133660)) in very large dart2js compile max heap capacity. + +* Fixed “Growth policy misinformed by concurrent sweep [#21364](https://github.com/dart-lang/sdk/issues/21364)” + +### [speed] 1.8x speedup in AOT compilation time of a large app + +contributors: [alexmarkov](https://github.com/alexmarkov), [mraleph](https://github.com/mraleph), [sigurdm](https://github.com/sigurdm), [cskau-g](https://github.com/cskau-g) + +* Commit [467c98](https://dart-review.googlesource.com/c/sdk/+/152100) + +* 1.8x speedup ([274s to 152s](https://dart-review.googlesource.com/c/sdk/+/152100)) in AOT compilation time of a large app ( — from-dill) + +### [speed] 5x-13x speedup on Dart RegExp matching in AOT mode + +contributors: [mraleph](https://github.com/mraleph), [rmacnak-google](https://github.com/rmacnak-google) + +* Commit [2c922b](https://dart-review.googlesource.com/c/sdk/+/151515) + +* 5x speedup improvement on benchmark from issue [42366](https://github.com/dart-lang/sdk/issues/42366) + +* 13x speedup ([17863744.009 us to 1301678.797 us](https://dart-review.googlesource.com/c/sdk/+/151515)) of RegExp(RunTime) benchmark on a Linux workstation + +* Fixed “RegExp is very slow in AOT mode [#42366](https://github.com/dart-lang/sdk/issues/42366)” + +### [speed] Up to 5x speedup in Dart UTF8 decoding + +contributors: [askeksa-google](https://github.com/askeksa-google), [mkustermann](https://github.com/mkustermann), [rakudrama](https://github.com/rakudrama) + +* Commit [cf6f89](https://dart-review.googlesource.com/c/sdk/+/148762), [e35ca3](https://dart-review.googlesource.com/c/sdk/+/150925), [78df4a](https://dart-review.googlesource.com/c/sdk/+/146785), [fa2fd4](https://dart-review.googlesource.com/c/sdk/+/142025) + +* [5.02x speedup](https://dart-review.googlesource.com/c/sdk/+/146785) in X64JIT TwoByteString benchmark + +* Fixed “Fast path Uint8List to _OneByteString conversion [#41703](https://github.com/dart-lang/sdk/issues/41703)”, “Write UTF-8 result directly to strings [#41704](https://github.com/dart-lang/sdk/issues/41704)” + +### [speed] 35–65% speedup in async/sync* functions + +contributors: [cskau-g](https://github.com/cskau-g), [mkustermann](https://github.com/mkustermann), [mraleph](https://github.com/mraleph) + +* Commit [e29407](https://dart-review.googlesource.com/c/sdk/+/146780), [fdca1f](https://dart-review.googlesource.com/c/sdk/+/134282), [7ed169](https://dart-review.googlesource.com/c/sdk/+/133586), [ca94ad](https://dart-review.googlesource.com/c/sdk/+/136190) + +* [35–65% speedup](https://dart-review.googlesource.com/c/sdk/+/133586) in Calls.IterableManualIterablePolymorphicManyYields across {dart,dart-aot}-{ia32,x64,armv7hf,armv8} + +* Fixed “improve performance of sync* code on VM [#37753](https://github.com/dart-lang/sdk/issues/37753)”, “Evaluate use of IndirectGoto when generating sync-yield function in VM [#37754](https://github.com/dart-lang/sdk/issues/37754)” + +### [speed] Up to 31.82% reduction in time with unboxed parameters and fields + +contributors: [mkustermann](https://github.com/mkustermann), [mkustermann](https://github.com/mkustermann) + +* Commit [9eb531](https://dart-review.googlesource.com/c/sdk/+/131824), [b95149](https://dart-review.googlesource.com/c/sdk/+/135313), [e076e7](https://dart-review.googlesource.com/c/sdk/+/132606), [9eb531](https://dart-review.googlesource.com/c/sdk/+/131824) + +* [Up to 31.82% reduction](https://dart-review.googlesource.com/c/sdk/+/131824) in matrix_utils_transform_rect_affine iteration time. + +* Fixed “Real unboxed fields in the Dart VM [#40004](https://github.com/dart-lang/sdk/issues/40004)” + +### [speed] 20–25% speedup on Dart Pointer<Int8,Int64,etc> loads/stores in AOT + +contributors: [dcharkes](https://github.com/dcharkes), [mkustermann](https://github.com/mkustermann) + +* Commit [408123](https://dart-review.googlesource.com/c/sdk/+/137788) + +* Speeds up Pointer<Int8,Int64,etc> loads/stores by [20–25%](https://dart-review.googlesource.com/c/sdk/+/137788) in AOT on x64. + +* Helped “stores and loads should support unboxed indices (in AOT) [#39432](https://github.com/dart-lang/sdk/issues/39432)” + +## Other improvements + +### [app size, energy, memory, speed] Add A/B test mode to local devicelab runner + +contributors: [yjbanov](https://github.com/yjbanov), [flar](https://github.com/flar), [ferhatb](https://github.com/ferhatb), [christopherfujino](https://github.com/christopherfujino), [liyuqian](https://github.com/liyuqian) + +* PR [#54494](https://github.com/flutter/flutter/pull/54494), [#56416](https://github.com/flutter/flutter/pull/56416), [#54676](https://github.com/flutter/flutter/pull/54676) + +### [app size] Fixed “iOS App Size in FAQ incorrect” + +contributors: [jmagman](https://github.com/jmagman), [xster](https://github.com/xster), [dnfield](https://github.com/dnfield), [sfshaza2](https://github.com/sfshaza2) + +* Issue [#38016](https://github.com/flutter/flutter/issues/38016), PR [#4080](https://github.com/flutter/website/pull/4080) + +### [app size] Enable the usage of — delete-tostring-package-uri in the flutter too + +contributors: [jonahwilliams](https://github.com/jonahwilliams), [dnfield](https://github.com/dnfield) + +* Issue [#53174](https://github.com/flutter/flutter/issues/53174), PR [#53273](https://github.com/flutter/flutter/pull/53273) + +### [energy, memory] Timeline summary now includes CPU/GPU/memory for iOS + +contributors: [iskakaushik](https://github.com/iskakaushik), [gaaclarke](https://github.com/gaaclarke), [albertwang0116](https://github.com/albertwang0116), [liyuqian](https://github.com/liyuqian), [chinmaygarde](https://github.com/chinmaygarde), [gw280](https://github.com/gw280) + +* Issue [#58803](https://github.com/flutter/flutter/issues/58803), PR [#18087](https://github.com/flutter/engine/pull/18087), [#18516](https://github.com/flutter/engine/pull/18516), [#18752](https://github.com/flutter/engine/pull/18752) + +* [http://flutter.dev/go/engine-cpu-profiling](http://flutter.dev/go/engine-cpu-profiling) + +### [memory] ImageCache flushes itself when told about memory pressure + +contributors: [dnfield](https://github.com/dnfield), [goderbauer](https://github.com/goderbauer), [chunhtai](https://github.com/chunhtai) + +* Issue [#16375](https://github.com/flutter/flutter/issues/16375), PR [#53959](https://github.com/flutter/flutter/pull/53959) + +### [memory] Fixed “Skia PathMask cache leaks memory” + +contributors: [bsalomon](https://github.com/bsalomon) + +* Issue [#50972](https://github.com/flutter/flutter/issues/50972), commit [4282d2](https://skia-review.googlesource.com/c/skia/+/272654/) + +### [memory] Fixed firebase_ml_vision memory leak on iOS + +contributors: [rishabnayak](https://github.com/rishabnayak), [bparrishmines](https://github.com/bparrishmines) + +* Issue [#32967](https://github.com/flutter/flutter/issues/32967), [#32652](https://github.com/flutter/flutter/issues/32652), PR [#1615](https://github.com/flutter/plugins/pull/1615) + +### [memory] Added DevTools based max memory test + +contributors: [liyuqian](https://github.com/liyuqian), [dnfield](https://github.com/dnfield), [digiter](https://github.com/digiter) + +* Issue [#44013](https://github.com/flutter/flutter/issues/44013), PR [#55486](https://github.com/flutter/flutter/pull/55486) + +### [memory] Defer image decoding when scrolling fast + +contributors: [dnfield](https://github.com/dnfield), [hixie](https://github.com/hixie), [goderbauer](https://github.com/goderbauer) + +* Issue [#44510](https://github.com/flutter/flutter/issues/44510), [#48775](https://github.com/flutter/flutter/issues/48775), PR [#49389](https://github.com/flutter/flutter/pull/49389) + +### [memory] If an image is available in the tree currently, it should be resolvable by key without reloading even if it didn’t fit into the image cache + +contributors: [dnfield](https://github.com/dnfield), [hixie](https://github.com/hixie), [goderbauer](https://github.com/goderbauer), [jonahwilliams](https://github.com/jonahwilliams), [ignatz](https://github.com/ignatz), [gaaclarke](https://github.com/gaaclarke) + +* Issue #[48731](https://github.com/flutter/flutter/issues/48731), [#49456](https://github.com/flutter/flutter/issues/49456), [#45406](https://github.com/flutter/flutter/issues/45406), PR [#50242](https://github.com/flutter/flutter/pull/50242), [#50297](https://github.com/flutter/flutter/pull/50297), [#50316](https://github.com/flutter/flutter/pull/50316), [#50318](https://github.com/flutter/flutter/pull/50318) + +* [https://flutter.dev/go/widget-tree-image-cache](https://flutter.dev/go/widget-tree-image-cache) + +### [speed] Rewrite DevTools frame chart with parallel UI and raster thread times + +contributors: [kenzieschmoll](https://github.com/kenzieschmoll), [terrylucas](https://github.com/terrylucas) + +* PR [#1999](https://github.com/flutter/devtools/pull/1999) + +### [speed] Added new Gallery mobile benchmarks + +contributors: [guidezpl](https://github.com/guidezpl), [liyuqian](https://github.com/liyuqian), [clocksmith](https://github.com/clocksmith), [rami-a](https://github.com/rami-a), [flar](https://github.com/flar) + +* PR [#55181](https://github.com/flutter/flutter/pull/55181), [#20](https://github.com/flutter/gallery/pull/20), [#100](https://github.com/flutter/gallery/pull/100), [#115](https://github.com/flutter/gallery/pull/115) + +### [speed] Add new Gallery web benchmarks + +contributors: [pennzht](https://github.com/pennzht), [yjbanov](https://github.com/yjbanov), [guidezpl](https://github.com/guidezpl), [willlarche](https://github.com/willlarche), [clocksmith](https://github.com/clocksmith), [josealba](https://github.com/josealba) + +* PR [#57576](https://github.com/flutter/flutter/pull/57576), [#163](https://github.com/flutter/gallery/pull/163) + +### [speed] Community added a new package to unlock 90 fps for some devices + +contributors: [ajinasokan](https://github.com/ajinasokan) + +* Issue [#35162](https://github.com/flutter/flutter/issues/35162) + +* [https://github.com/ajinasokan/flutter_displaymode](https://github.com/ajinasokan/flutter_displaymode) + +### [speed] Renamed GPU thread to raster thread + +contributors: [filiph](https://github.com/filiph), [liyuqian](https://github.com/liyuqian), [kenzieschmoll](https://github.com/kenzieschmoll), [chinmaygarde](https://github.com/chinmaygarde), [cbracken](https://github.com/cbracken), [goderbauer](https://github.com/goderbauer), [zanderso](https://github.com/zanderso), [devoncarew](https://github.com/devoncarew), [sfshaza2](https://github.com/sfshaza2) + +* Issue [#29443](https://github.com/flutter/flutter/issues/29443), PR [#17148](https://github.com/flutter/engine/pull/17148), [#17329](https://github.com/flutter/engine/pull/17329), [#17408](https://github.com/flutter/engine/pull/17408), [#53422](https://github.com/flutter/flutter/pull/53422), [#1758](https://github.com/flutter/devtools/pull/1758), [#3836](https://github.com/flutter/website/pull/3836) + +### [speed] Added benchmark for platform views + +contributors: [blasten](https://github.com/blasten), [cyanglaz](https://github.com/cyanglaz) + +* Issue [52684](https://github.com/flutter/flutter/issues/52684), PR [#52717](https://github.com/flutter/flutter/pull/52717) + +### [speed] Avoid unnecessary thread clock clock calls while tracing, resulting in less (kernel) trace buffer space usage, and slightly lower tracing overhead. + +contributors: [nathanrogersgoogle](https://github.com/nathanrogersgoogle), [a-siva](https://github.com/a-siva), [fmeawad](https://github.com/fmeawad), [johngro](https://github.com/johngro) + +* [https://dart-review.googlesource.com/c/sdk/+/149395](https://dart-review.googlesource.com/c/sdk/+/149395) + +### [speed] Fix RefreshIndicator performance issue + +contributors: [dnfield](https://github.com/dnfield), [goderbauer](https://github.com/goderbauer) + +* Issue [#47664](https://github.com/flutter/flutter/issues/47664), PR [#53206](https://github.com/flutter/flutter/pull/53206) + +### [speed] Enable lazy async stack traces via tool feature + +contributors: [jonahwilliams](https://github.com/jonahwilliams), [zanderso](https://github.com/zanderso) + +* Issue [#48725](https://github.com/flutter/flutter/issues/48725), PR [#49377](https://github.com/flutter/flutter/pull/49377) + +### [speed] [flutter_tools] initialize frontend server with build + +contributors: [jonahwilliams](https://github.com/jonahwilliams), [zanderso](https://github.com/zanderso) + +* Issue [#40388](https://github.com/flutter/flutter/issues/40388), [#49865](https://github.com/flutter/flutter/issues/49865), [#43464](https://github.com/flutter/flutter/issues/43464), PR [#51477](https://github.com/flutter/flutter/pull/51477) + +### [speed] Colored box and container optimization + +contributors: [dnfield](https://github.com/dnfield), [hansmuller](https://github.com/hansmuller), [piinks](https://github.com/piinks), [redbrogdon](https://github.com/redbrogdon), [goderbauer](https://github.com/goderbauer), [liyuqian](https://github.com/liyuqian) + +* Issue [#9672](https://github.com/flutter/flutter/issues/9672), PR [#50979](https://github.com/flutter/flutter/pull/50979) + +## Conclusion + +Due to the lowered productivity as a result of COVID-19, we expected to have fewer performance improvements. Surprisingly, we found more quantified improvements in Q1-Q2 2020 than Q3-Q4 2019 (25 vs. 20) thanks to our growing community! Overall, we see 49 improvements from 78 contributors in Q1-Q2 2020. + +Despite our best effort, some performance contributions in Q1-Q2 2020 may be missed in this update. Please don’t hesitate to let us know of any missing contributions, and we’ll put them in the next update. \ No newline at end of file diff --git a/sites/www/content/blog/flutter-platform-channels/images/1BIknuDE2gMHmbYg7S8_F0Q.webp b/sites/www/content/blog/flutter-platform-channels/images/1BIknuDE2gMHmbYg7S8_F0Q.webp new file mode 100644 index 0000000000..441408d8b3 Binary files /dev/null and b/sites/www/content/blog/flutter-platform-channels/images/1BIknuDE2gMHmbYg7S8_F0Q.webp differ diff --git a/sites/www/content/blog/flutter-platform-channels/images/1Sd6s3EDGkU8TBS9xLc4Zvw.webp b/sites/www/content/blog/flutter-platform-channels/images/1Sd6s3EDGkU8TBS9xLc4Zvw.webp new file mode 100644 index 0000000000..322b134af1 Binary files /dev/null and b/sites/www/content/blog/flutter-platform-channels/images/1Sd6s3EDGkU8TBS9xLc4Zvw.webp differ diff --git a/sites/www/content/blog/flutter-platform-channels/images/1jd9Thys5_k-jbkKM7P69Ng.webp b/sites/www/content/blog/flutter-platform-channels/images/1jd9Thys5_k-jbkKM7P69Ng.webp new file mode 100644 index 0000000000..794a89996e Binary files /dev/null and b/sites/www/content/blog/flutter-platform-channels/images/1jd9Thys5_k-jbkKM7P69Ng.webp differ diff --git a/sites/www/content/blog/flutter-platform-channels/images/1ykNghfAKtx0xsZWedfgslg.webp b/sites/www/content/blog/flutter-platform-channels/images/1ykNghfAKtx0xsZWedfgslg.webp new file mode 100644 index 0000000000..1954c171e8 Binary files /dev/null and b/sites/www/content/blog/flutter-platform-channels/images/1ykNghfAKtx0xsZWedfgslg.webp differ diff --git a/sites/www/content/blog/flutter-platform-channels/index.md b/sites/www/content/blog/flutter-platform-channels/index.md new file mode 100644 index 0000000000..de97bbdd1c --- /dev/null +++ b/sites/www/content/blog/flutter-platform-channels/index.md @@ -0,0 +1,763 @@ +--- +title: "Flutter Platform Channels" +description: "“Nice UI. But how does Flutter deal with platform-specific APIs?”" +publishDate: 2018-08-28 +author: mravn +image: images/1ykNghfAKtx0xsZWedfgslg.webp +category: announcements +layout: blog +--- + +[Flutter](https://flutter.io) invites you to write your mobile app in the Dart programming language and build for both Android and iOS. But Dart does not compile to Android’s Dalvik bytecode, nor are you blessed with Dart/Objective-C bindings on iOS. This means that your Dart code is written without direct access to the platform-specific APIs of iOS Cocoa Touch and the Android SDK. + +This isn’t much of a problem as long as you are just writing Dart to paint pixels on the screen. The Flutter framework and its underlying graphics engine are very capable of that on their own. Nor is it a problem if everything you do besides painting pixels is file or network I/O and associated business logic. The Dart language, runtime, and libraries have you covered there. + +But non-trivial apps require deeper integration with the host platform: + +* notifications, app lifecycle, deep links, … + +* sensors, camera, battery, geolocation, sound, connectivity, … + +* sharing information with other apps, launching other apps, … + +* persisted preferences, special folders, device information, … + +The list is long and broad and seems to grow with every platform release. + +Access to all of these platform APIs could be baked into the Flutter framework itself. But that would make Flutter a lot bigger and give it many more reasons to change. In practice, that would likely lead to Flutter lagging behind the latest platform release. Or landing app authors with unsatisfactory “least common denominator” wrappings of platform APIs. Or puzzling newcomers with unwieldy abstractions to paper over platform differences. Or version fragmentation. Or bugs. + +Come to think of it, probably all of the above. + +The Flutter team chose a different approach. It doesn’t do all that much, but it is simple, versatile, and completely in your hands. + +First of all, **Flutter is hosted** by an ambient Android or iOS app. The Flutter parts of the app are wrapped in standard platform-specific components such as `View` on Android and `UIViewController` on iOS. So while Flutter invites you to write your app in Dart, you can do as much or as little in Java/Kotlin or Objective-C/Swift as you please in the host app, working directly on top of platform-specific APIs. + +Second, [**platform channels**](https://flutter.io/platform-channels/) provide a simple mechanism for communicating between your Dart code and the platform-specific code of your host app. This means you can expose a platform service in your host app code and have it invoked from the Dart side. Or vice versa. + +And third, [**plugins**](https://flutter.io/developing-packages/) make it possible to create a Dart API backed by an Android implementation written in Java or Kotlin and an iOS implementation written in Objective-C or Swift — and package that up as a Flutter/Android/iOS triple glued together using platform channels. This means you can reuse, share, and distribute *your take* on how Flutter should use a particular platform API. + +This article is an in-depth introduction to platform channels. Starting from Flutter’s messaging foundations, I’ll introduce the message/method/event channel concepts, and discuss some API design considerations. There’ll be no API listings, but short code samples for copy-paste reuse instead. A brief list of usage guidelines is provided, based on my experience contributing to the [flutter/plugins](https://github.com/flutter/plugins) GitHub repository as a member of the Flutter team. The article concludes with a list of additional resources, including links to the DartDoc/JavaDoc/ObjcDoc reference APIs. + +### Table of contents + +[**Platform channels API**](https://medium.com/p/ce7f540a104e#99ef) +[Foundations: asynchronous, binary messaging](https://medium.com/p/ce7f540a104e#67d0) +[Message channels: name + codec](https://medium.com/p/ce7f540a104e#5fc7) +[Method channels: standardized envelopes](https://medium.com/p/ce7f540a104e#53af) +[Event channels: streaming](https://medium.com/p/ce7f540a104e#03ed) + +[**Usage guidelines**](https://medium.com/p/ce7f540a104e#7547) +[Prefix channel names by domain for uniqueness](https://medium.com/p/ce7f540a104e#2765) +[Consider treating platform channels as intra-module communication](https://medium.com/p/ce7f540a104e#3615) +[Don’t mock platform channels](https://medium.com/p/ce7f540a104e#8ab5) +[Consider automated testing for your platform interaction](https://medium.com/p/ce7f540a104e#bc56) +[Keep platform side ready for incoming synchronous calls](https://medium.com/p/ce7f540a104e#a3e8) + +[**Resources**](https://medium.com/p/ce7f540a104e#39c4) + +## Platform channels API + +For most use cases you would probably employ *method channels* for platform communication. But since many of their properties are derived from the simpler *message channels* and from the underlying *binary messaging* foundations, I’ll start there. + +### Foundations: asynchronous, binary messaging + +<DashImage figure src="images/1BIknuDE2gMHmbYg7S8_F0Q.webp" /> + + +At the most basic level, Flutter talks to platform code using asynchronous message passing with binary messages — meaning the message payload is a byte buffer. To distinguish between messages used for different purposes, each message is sent on a logical “channel” which is just a name string. The examples below use the channel name `foo`. + +``` +*// Send a binary message from Dart to the platform.* + +final WriteBuffer buffer = WriteBuffer() + ..putFloat64(3.1415) + ..putInt32(12345678); +final ByteData message = buffer.done(); +await BinaryMessages.send('foo', message); +print('Message sent, reply ignored'); +``` + + +On Android such a message can be received, as a `java.nio.ByteBuffer`, using the following Kotlin code: + +``` +*// Receive binary messages from Dart on Android. +// This code can be added to a FlutterActivity subclass, typically +// in onCreate.* + +flutterView.setMessageHandler("foo") { message, reply -> + message.order(ByteOrder.nativeOrder()) + val x = message.double + val n = message.int + Log.i("MSG", "Received: $x and $n") + reply.reply(null) +} +``` + + +The `ByteBuffer` API supports reading off primitive values while automatically advancing the current read position. The story on iOS is similar; suggestions for improving my weak Swift fu are very welcome: + +``` +*// Receive binary messages from Dart on iOS.* +*// This code can be added to a FlutterAppDelegate subclass, +// typically in application:didFinishLaunchingWithOptions:.* + +let flutterView = + window?.rootViewController as! FlutterViewController; +flutterView.setMessageHandlerOnChannel("foo") { + (message: Data!, reply: FlutterBinaryReply) -> Void in + let x : Float64 = message.subdata(in: 0..<8) + .withUnsafeBytes { $0.pointee } + let n : Int32 = message.subdata(in: 8..<12) + .withUnsafeBytes { $0.pointee } + os_log("Received %f and %d", x, n) + reply(nil) +} +``` + + +Communication is bidirectional, so you can send messages in the opposite direction too, from Java/Kotlin or Objective-C/Swift to Dart. Reversing the direction of the above setup looks as follows: + +``` +*// Send a binary message from Android.* + +val message = ByteBuffer.allocateDirect(12) +message.putDouble(3.1415) +message.putInt(123456789) +flutterView.send("foo", message) { _ -> + Log.i("MSG", "Message sent, reply ignored") +} + + +// Send a binary message from iOS. + +var message = Data(capacity: 12) +var x : Float64 = 3.1415 +var n : Int32 = 12345678 +message.append(UnsafeBufferPointer(start: &x, count: 1)) +message.append(UnsafeBufferPointer(start: &n, count: 1)) +flutterView.send(onChannel: "foo", message: message) {(_) -> Void in + os_log("Message sent, reply ignored") +} + + +// Receive binary messages from the platform. + +BinaryMessages.setMessageHandler('foo', (ByteData message) async { + final ReadBuffer readBuffer = ReadBuffer(message); + final double x = readBuffer.getFloat64(); + final int n = readBuffer.getInt32(); + print('Received $x and $n'); + return null; +}); +``` + + +**The fine print.** *Mandatory replies.* Each message send involves an asynchronous reply from the receiver. In the examples above, there is no interesting value to communicate back, but the null reply is necessary for the Dart future to complete and for the two platform callbacks to execute. + +*Threads.* Messages and replies are received, and must be sent, on the platform’s main UI thread. In Dart there is only one thread per Dart isolate, i.e. per Flutter view, so there is no confusion about which thread to use here. + +*Exceptions.* Any uncaught exception thrown in a Dart or Android message handler is caught by the framework, logged, and a null reply is sent back to the sender. Uncaught exceptions thrown in reply handlers are logged. + +*Handler lifetime.* Registered message handlers are retained and kept alive along with the Flutter view (meaning the Dart isolate, the Android `FlutterView` instance, and the iOS `FlutterViewController`). You can cut a handler’s life short by deregistering it: just set a null (or different) handler using the same channel name. + +*Handler uniqueness.* Handlers are held in a hash map keyed by channel name, so there can be at most one handler per channel. A message sent on a channel for which no message handler is registered at the receiving end is responded to automatically, using a null reply. + +*Synchronous communication.* Platform communication is available in asynchronous mode only. This avoids making blocking calls across threads and the system-level problems that might entail (poor performance, risk of deadlocks). At the time of this writing, it is not entirely clear if synchronous communication is really needed in Flutter and, if so, in what form. + +Working at the level of binary messages, you need to worry about delicate details like endianness and how to represent higher-level messages such as strings or maps using bytes. You also need to specify the right channel name whenever you want to send a message or register a handler. Making this easier leads us to platform channels: + +*A platform channel is an object that brings together a channel name and a codec for serializing/deserializing messages to binary form and back*. + +### Message channels: name + codec + +<DashImage figure src="images/1Sd6s3EDGkU8TBS9xLc4Zvw.webp" /> + + +Suppose you want to send and receive string messages instead of byte buffers. This can be done using a message channel, a simple kind of platform channel, constructed with a string codec. The code below shows how to use message channels in both directions across Dart, Android, and iOS: + +``` +*// String messages* + +// Dart side + +const channel = BasicMessageChannel<String>('foo', StringCodec()); + +// Send message to platform and receive reply. +final String reply = await channel.send('Hello, world'); +print(reply); + +// Receive messages from platform and send replies. +channel.setMessageHandler((String message) async { + print('Received: $message'); + return 'Hi from Dart'; +}); + + +// Android side + +val channel = BasicMessageChannel<String>( + flutterView, "foo", StringCodec.INSTANCE) + +// Send message to Dart and receive reply. +channel.send("Hello, world") { reply -> + Log.i("MSG", reply) +} + +// Receive messages from Dart and send replies. +channel.setMessageHandler { message, reply -> + Log.i("MSG", "Received: $message") + reply.reply("Hi from Android") +} + + +// iOS side + +let channel = FlutterBasicMessageChannel( + name: "foo", + binaryMessenger: controller, + codec: FlutterStringCodec.sharedInstance()) + +// Send message to Dart and receive reply. +channel.sendMessage("Hello, world") {(reply: Any?) -> Void in + os_log("%@", type: .info, reply as! String) +} + +// Receive messages from Dart and send replies. +channel.setMessageHandler { + (message: Any?, reply: FlutterReply) -> Void in + os_log("Received: %@", type: .info, message as! String) + reply("Hi from iOS") +} +``` + + +The channel name is specified only on channel construction. After that, calls to send a message or set a message handler can be done without repeating the channel name. More importantly, we leave it to the string codec class to deal with how to interpret bytes buffers as strings and vice versa. + +These are noble advantages to be sure, but you’d probably agree that `BasicMessageChannel` doesn’t do all that much. Which is on purpose. The Dart code above is equivalent to the following use of the binary messaging foundations: + +``` +**const** codec = StringCodec(); + +// Send message to platform and receive reply. +final String reply = codec.decodeMessage( + await BinaryMessages.send( + 'foo', + codec.encodeMessage('Hello, world'), + ), +); +print(reply); + +// Receive messages from platform and send replies. +BinaryMessages.setMessageHandler('foo', (ByteData message) async { + print('Received: ${codec.decodeMessage(message)}'); + return codec.encodeMessage('Hi from Dart'); +}); +``` + + +This remark applies to the [Android](https://docs.flutter.io/javadoc/io/flutter/plugin/common/BasicMessageChannel.html) and [iOS](https://docs.flutter.io/objcdoc/Classes/FlutterBasicMessageChannel.html) implementations of message channels as well. *There is no magic involved*: + +* Message channels delegate to the binary messaging layer for all communication. + +* Message channels do not keep track of registered handlers themselves. + +* Message channels are light-weight and stateless. + +* Two message channel instances created with the same channel name and codec are equivalent (and interfere with each other’s communication). + +For various historical reasons, the Flutter framework defines four different message codecs: + +* [`StringCodec`](https://docs.flutter.io/flutter/services/StringCodec-class.html) Encodes strings using UTF-8. As we’ve just seen, message channels with this codec have type `BasicMessageChannel<String>` in Dart. + +* [`BinaryCodec`](https://docs.flutter.io/flutter/services/BinaryCodec-class.html) Implementing the identity mapping on byte buffers, this codec allows you to enjoy the convenience of channel objects in cases where you don’t need encoding/decoding. Dart message channels with this codec have type `BasicMessageChannel<ByteData>`. + +* [`JSONMessageCodec`](https://docs.flutter.io/flutter/services/JSONMessageCodec-class.html) Deals in “JSON-like” values (strings, numbers, Booleans, null, lists of such values, and string-keyed maps of such values). Lists and maps are heterogeneous and can be nested. During encoding, the values are turned into JSON strings and then to bytes using UTF-8. Dart message channels have type `BasicMessageChannel<dynamic>` with this codec. + +* [`StandardMessageCodec`](https://docs.flutter.io/flutter/services/StandardMessageCodec-class.html) Deals in slightly more generalized values than the JSON codec, supporting also homogeneous data buffers (`UInt8List`, `Int32List`, `Int64List`, `Float64List`) and maps with non-string keys. The handling of numbers differs from JSON with Dart ints arriving as 32 or 64 bit signed integers on the platform, depending on magnitude — never as floating-point numbers. Values are encoded into a custom, reasonably compact, and extensible binary format. The standard codec is designed to be the default choice for channel communication in Flutter. As for JSON, Dart message channels constructed with the standard codec have type `BasicMessageChannel<dynamic>`. + +As you may have guessed, message channels work with any message codec implementation that satisfies a simple contract. This allows you to plug in your own codec, if you need to. You’ll have to implement compatible encoding and decoding in Dart, Java/Kotlin, and Objective-C/Swift. + +**The fine print.** *Codec evolution*. Each message codec is available in Dart, as part of the Flutter framework, as well as on both platforms, as part of the libraries exposed by Flutter to your Java/Kotlin or Objective-C/Swift code. Flutter uses the codecs only for intra-app communication, not as a persistence format. This means that the binary form of messages may change from one release of Flutter to the next, without warning. Of course, the Dart, Android, and iOS codec implementations are evolved together, to ensure that what is encoded by the sender can be successfully decoded by the receiver, in both directions. + +*Null messages.* Any message codec must support and preserve null messages since that is the default reply to a message sent on a channel for which no message handler has been registered on the receiving side. + +*Static typing of messages in Dart.* A message channel configured with the standard message codec gives type `dynamic` to messages and replies. You’d often make your type expectations explicit by assigning to a typed variable: + +``` +**final** String reply1 = **await** channel.send(msg1); +**final** int reply2 = **await** channel.send(msg2); +``` + + +But there’s a caveat when dealing with replies involving generic type parameters: + +``` +**final** List<String> reply3 = **await** channel.send(msg3); *// Fails.* +**final** List<dynamic> reply3 = **await** channel.send(msg3); *// Works.* +``` + + +The first line fails at runtime, unless the reply is null. The standard message codec is written for heterogeneous lists and maps. On the Dart side, these have runtime types `List<dynamic>` and `Map<dynamic, dynamic>`, and Dart 2 prevents such values from being assigned to variables with more specific type arguments. This situation is similar to Dart JSON deserialization which produces `List<dynamic>` and `Map<String, dynamic>` — as does the JSON message codec. + +Futures can get you into similar trouble: + +``` +Future<String> greet() => channel.send('hello, world'); *// Fails.* +Future<String> greet() **async** { *// Works.* + **final** String reply = **await** channel.send('hello, world'); + **return** reply; +} +``` + + +The first method fails at runtime, even if the reply received is a string. The channel implementation creates a `Future<dynamic>` regardless of the type of the reply, and such an object cannot be assigned to a `Future<String>`. + +*Why the “basic“ in BasicMessageChannel?* Message channels seem to be used only in rather restricted situations where you are communicating some form of homogeneous event stream in an implied context. Like keyboard events, perhaps. For most applications of platform channels, you’re going to need to communicate not only values, but also what you want to happen with each value, or how you’d like it to be interpreted by the receiver. One way to do that is to have the message represent a method call with the value as argument. So you’ll want a standard way of separating the method name from the argument in the message. And you’ll also want a standard way to distinguish between success and error replies. This is what *method* channels do for you. Now, `BasicMessageChannel` was originally named `MessageChannel`, but was renamed to avoid confusing `MessageChannel` with `MethodChannel` in code. Being more generally applicable, method channels kept the shorter name. + +### Method channels: standardized envelopes + +<DashImage figure src="images/1ykNghfAKtx0xsZWedfgslg.webp" /> + + +Method channels are platform channels designed for invoking named pieces of code across Dart and Java/Kotlin or Objective-C/Swift. Method channels make use of standardized message “envelopes” to convey method name and arguments from sender to receiver, and to distinguish between successful and erroneous results in the associated reply. The envelopes and supported payload are defined by separate method codec classes, similarly to how message channels use message codecs. + +*This is all that method channels do: combine a channel name with a codec.* + +In particular, no assumptions are being made about what code is executed on receipt of a message on a method channel. Even though the message represents a method call, you don’t have to invoke a method. You might just switch on the method name and execute a few lines of code for each case. + +**Side note.** This lack of implied or automated binding to methods and their parameters might disappoint you. That’s fine, disappointment can be productive. I suppose you can build such a solution from scratch using annotation processing and code generation, or maybe you can reuse parts of an existing RPC framework. Flutter is open source, [feel free to contribute](https://github.com/flutter/flutter/blob/master/CONTRIBUTING.md)! Method channels are available as a target for your code generation, if they fit the bill. In the mean time, they are useful on their own in “handcraft mode”. + +Method channels were the Flutter team’s answer to the challenge of defining a workable communication API for use by the, at the time, non-existing plugin ecosystem. We wanted something that plugin authors could start using right away, without a lot of boilerplate or complicated build setup. I think the method channel concept makes a decent answer, but I’d be surprised if it remains the *only* answer. + +Here’s how you would use a method channel in the simple case of invoking a bit of platform code from Dart. The code is associated with the name `bar` which is not a method name in this case, but could have been. All it does is construct a greeting string and return it to the caller, so we can code this with the reasonable assumption that the platform invocation won’t fail (we’ll look at error handling further below): + +``` +*// Invocation of platform methods, simple case.* + +// Dart side. + +const channel = MethodChannel('foo'); +final String greeting = await channel.invokeMethod('bar', 'world'); +print(greeting); + + +// Android side. + +val channel = MethodChannel(flutterView, "foo") +channel.setMethodCallHandler { call, result -> + when (call.method) { + "bar" -> result.success("Hello, ${call.arguments}") + else -> result.notImplemented() + } +} + +// iOS side. + +let channel = FlutterMethodChannel( + name: "foo", binaryMessenger: flutterView) +channel.setMethodCallHandler { + (call: FlutterMethodCall, result: FlutterResult) -> Void in + switch (call.method) { + case "bar": result("Hello, \(call.arguments as! String)") + default: result(FlutterMethodNotImplemented) + } +} +``` + + +By adding cases to the switch constructs, we can easily extend the above to handle multiple methods. The default clause handles the situation where an unknown method is called (most likely due to a programming error). + +The Dart code above is equivalent to the following: + +``` +**const** codec = StandardMethodCodec(); + +final ByteData reply = await BinaryMessages.send( + 'foo', + codec.encodeMethodCall(MethodCall('bar', 'world')), +); +if (reply == null) + throw MissingPluginException(); +else + print(codec.decodeEnvelope(reply)); +``` + + +The [Android](https://docs.flutter.io/javadoc/io/flutter/plugin/common/MethodChannel.html) and [iOS](https://docs.flutter.io/objcdoc/Classes/FlutterMethodChannel.html) implementations of method channels are similarly thin wrappers around calls to the binary messaging foundations. A null reply is used to represent a “not implemented” result. This conveniently makes the behavior at the receiving end indifferent to whether the invocation fell through to the default clause in the switch, or no method call handler had been registered with the channel at all. + +The argument value in the example is the single string `world`. But the default method codec, aptly named the “standard method codec”, uses the standard *message* codec under the hood to encode payload values. This means that the “generalized JSON-like” values described earlier are all supported as method arguments and (successful) results. In particular, heterogeneous lists support multiple arguments, while heterogeneous maps support named arguments. The default arguments value is null. A few examples: + +``` +**await** channel.invokeMethod('bar'); +**await** channel.invokeMethod('bar', <dynamic>['world', 42, pi]); +**await** channel.invokeMethod('bar', <String, dynamic>{ + name: 'world', + answer: 42, + math: pi, +})); +``` + + +The Flutter SDK includes two method codecs: + +* [`StandardMethodCodec`](https://docs.flutter.io/flutter/services/StandardMethodCodec-class.html) which by default delegates the encoding of payload values to `StandardMessageCodec`. Because the latter is extensible, so is the former. + +* [`JSONMethodCodec`](https://docs.flutter.io/flutter/services/JSONMethodCodec-class.html) which delegates the encoding of payload values to `JSONMessageCodec`. + +You can configure method channels with any method codec, including custom ones. To fully understand what is involved in implementing a codec, let’s look at how errors are handled at the method channel API level by extending the example above with a fallible `baz` method: + +``` +*// Method calls with error handling.* + +// Dart side. + +const channel = MethodChannel('foo'); + +// Invoke a platform method. +const name = 'bar'; // or 'baz', or 'unknown' +const value = 'world'; +try { + print(await channel.invokeMethod(name, value)); +} on PlatformException catch(e) { + print('$name failed: ${e.message}'); +} on MissingPluginException { + print('$name not implemented'); +} + +// Receive method invocations from platform and return results. +channel.setMethodCallHandler((MethodCall call) async { + switch (call.method) { + case 'bar': + return 'Hello, ${call.arguments}'; + case 'baz': + throw PlatformException(code: '400', message: 'This is bad'); + default: + throw MissingPluginException(); + } +}); + + +// Android side. + +val channel = MethodChannel(flutterView, "foo") + +// Invoke a Dart method. +val name = "bar" // or "baz", or "unknown" +val value = "world" +channel.invokeMethod(name, value, object: MethodChannel.Result { + override fun success(result: Any?) { + Log.i("MSG", "$result") + } + override fun error(code: String?, msg: String?, details: Any?) { + Log.e("MSG", "$name failed: $msg") + } + override fun notImplemented() { + Log.e("MSG", "$name not implemented") + } +}) + +// Receive method invocations from Dart and return results. +channel.setMethodCallHandler { call, result -> + when (call.method) { + "bar" -> result.success("Hello, ${call.arguments}") + "baz" -> result.error("400", "This is bad", null) + else -> result.notImplemented() + } +} + + +// iOS side. + +let channel = FlutterMethodChannel( + name: "foo", binaryMessenger: flutterView) + +// Invoke a Dart method. +let name = "bar" // or "baz", or "unknown" +let value = "world" +channel.invokeMethod(name, arguments: value) { + (result: Any?) -> Void in + if let error = result as? FlutterError { + os_log("%@ failed: %@", type: .error, name, error.message!) + } else if FlutterMethodNotImplemented.isEqual(result) { + os_log("%@ not implemented", type: .error, name) + } else { + os_log("%@", type: .info, result as! NSObject) + } +} + +// Receive method invocations from Dart and return results. +channel.setMethodCallHandler { + (call: FlutterMethodCall, result: FlutterResult) -> Void in + switch (call.method) { + case "bar": result("Hello, \(call.arguments as! String)") + case "baz": result(FlutterError( + code: "400", message: "This is bad", details: nil)) + default: result(FlutterMethodNotImplemented) +} +``` + + +Errors are triples (code, message, details) where the code and message are strings. The message is intended for human consumption, the code for, well, code. The error details is some custom value, often null, which is constrained only by the kinds of value that the codec supports. + +**The fine print.** *Exceptions.* Any uncaught exception thrown in a Dart or Android method call handler is caught by the channel implementation, logged, and an error result is returned to the caller. Uncaught exceptions thrown in result handlers are logged. + +*Envelope encoding.* How a method codec encodes its envelopes is an implementation detail just like how message codecs convert messages to bytes. As an example, a method codec might use lists: method calls can be encoded as a two-element lists [method name, arguments]; success results as one-element lists [result]; error results as three-element lists [code, message, details]. Such a method codec can then be implemented simply by delegation to an underlying *message* codec that supports at least lists, strings, and null. The method call arguments, success results, and error details would be arbitrary values supported by that message codec. + +*API differences.* The code examples above highlight that method channels deliver results very differently across Dart, Android, and iOS: + +* On the Dart side, invocation is handled by a method returning a future. The future completes with the result of the call in success cases, with a `PlatformException` in error cases, and with a `MissingPluginException` in the not implemented case. + +* On Android, invocation is handled by a method taking a callback argument. The callback interface defines three methods of which one is called, depending on the outcome. Client code implements the callback interface to define what should happen on success, on error, and on not implemented. + +* On iOS, invocation is similarly handled by a method taking a callback argument. But here, the callback is a single-argument function which is given either a `FlutterError` instance, the `FlutterMethodNotImplemented` constant, or, in case of success, the result of the invocation. Client code provides a block with conditional logic to handle the different cases, as needed. + +These differences, mirrored also in the way message call handlers are written, arose as concessions to the styles of the programming languages (Dart, Java, and Objective-C) used for the Flutter SDK method channel implementations. Redoing the implementations in Kotlin and Swift might remove some of the differences, but care must be taken to avoid making it harder to use method channels from Java and Objective-C. + +### Event channels: streaming + +<DashImage figure src="images/1jd9Thys5_k-jbkKM7P69Ng.webp" /> + + +An event channel is a specialized platform channel intended for the use case of exposing platform events to Flutter as a Dart stream. The Flutter SDK currently has no support for the symmetrical case of exposing Dart streams to platform code, though that could be built, if the need arises. + +Here’s how you would consume a platform event stream on the Dart side: + +``` +*// Consuming events on the Dart side.* + +const channel = EventChannel('foo'); + +channel.receiveBroadcastStream().listen((dynamic event) { + print('Received event: $event'); +}, onError: (dynamic error) { + print('Received error: ${error.message}'); +}); +``` + + +The code below shows how to produce events on the platform side, using sensor events on Android as an example. The main concern is to ensure that we are listening to events from the platform source (the sensor manager in this case) and sending them through the event channel precisely when 1) there is at least one stream listener on the Dart side and 2) the ambient `Activity` is running. Packaging up the necessary logic in a single class increases the chance of doing this correctly: + +``` +*// Producing sensor events on Android.* + +// SensorEventListener/EventChannel adapter. +class SensorListener(private val sensorManager: SensorManager) : + EventChannel.StreamHandler, SensorEventListener { + private var eventSink: EventChannel.EventSink? = null + + // EventChannel.StreamHandler methods + override fun onListen( + arguments: Any?, eventSink: EventChannel.EventSink?) { + this.eventSink = eventSink + registerIfActive() + } + override fun onCancel(arguments: Any?) { + unregisterIfActive() + eventSink = null + } + + // SensorEventListener methods. + override fun onSensorChanged(event: SensorEvent) { + eventSink?.success(event.values) + } + override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) { + if (accuracy == SensorManager.SENSOR_STATUS_ACCURACY_LOW) + eventSink?.error("SENSOR", "Low accuracy detected", null) + } + + // Lifecycle methods. + fun registerIfActive() { + if (eventSink == null) return + sensorManager.registerListener( + this, + sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE), + SensorManager.SENSOR_DELAY_NORMAL) + } + fun unregisterIfActive() { + if (eventSink == null) return + sensorManager.unregisterListener(this) + } +} + +// Use of the above class in an Activity. +class MainActivity: FlutterActivity() { + var sensorListener: SensorListener? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + GeneratedPluginRegistrant.registerWith(this) + sensorListener = SensorListener( + getSystemService(Context.SENSOR_SERVICE) as SensorManager) + val channel = EventChannel(flutterView, "foo") + channel.setStreamHandler(sensorListener) + } + + override fun onPause() { + sensorListener?.unregisterIfActive() + super.onPause() + } + + override fun onResume() { + sensorListener?.registerIfActive() + super.onResume() + } +} +``` + + +If you use the `android.arch.lifecycle` package in your app, you could make `SensorListener` more self-contained by making it a `LifecycleObserver`. + +**The fine print.** *Life of a stream handler.* The platform side stream handler has two methods, `onListen` and `onCancel`, which are invoked whenever the number of listeners to the Dart stream goes from zero to one and back, respectively. This can happen multiple times. The stream handler implementation is supposed to start pouring events into the event sink when the former is called, and stop when the latter is called. In addition, it should pause when the ambient app component is not running. The code above provides a typical example. Under the covers, a stream handler is of course just a binary message handler, registered with the Flutter view using the event channel’s name. + +*Codec.* An event channel is configured with a method codec, allowing us to distinguish between success and error events in the same way that method channels are able to distinguish between success and error results. + +*Stream handler arguments and errors.* The `onListen` and `onCancel` stream handler methods are invoked via method channel invocations. So we have control method calls from Dart to the platform and event messages in the reverse direction, all on the same logical channel. This setup allows arguments to be relayed to both control methods and any errors to be reported back. On the Dart side, the arguments, if any, are given in the call to `receiveBroadcastStream`. This means they are specified only once, regardless of the number of invocations of `onListen` and `onCancel` happening during the lifetime of the stream. Any errors reported back are logged. + +*End of stream.* An event sink has an `endOfStream` method that can be invoked to signal that no additional success or error events will be sent. The null binary message is used for this purpose. On receipt on the Dart side, the stream is closed. + +*Life of a stream*. The Dart stream is backed by a stream controller fed from the incoming platform channel messages. A binary message handler is registered using the event channel’s name to receive incoming messages only while the stream has listeners. + +## Usage guidelines + +### Prefix channel names by domain for uniqueness + +Channel names are just strings, but they have to be unique across all channel objects used for different purposes in your app. You can accomplish that using any suitable naming scheme. However, the recommended approach *for channels used in plugins* is to employ a domain name and plugin name prefix such as `some.body.example.com/sensors/foo` for the `foo` channel used by the `sensors` plugin developed by `some.body` at `example.com`. Doing so allows plugin consumers to combine any number of plugins in their apps without risk of channel name collisions. + +### Consider treating platform channels as intra-module communication + +Code for invoking remote procedure calls in distributed systems look superficially similar to code using method channels: you invoke a method given by a string and serialize your arguments and results. Since distributed system components are often developed and deployed independently, robust request and reply checking is critical, and usually done in check-and-log style on both sides of the network. + +Platform channels on the other hand glue together three pieces of code that are developed and deployed together, in a single component. + +Java/Kotlin ↔ Dart ↔ Objective-C/Swift + +In fact, it very often makes sense to package up a triad like this in a *single code module*, such as a Flutter plugin. This means that the need for arguments and results checking across method channel invocations should be comparable to the need for such checks across normal method calls within the same module. + +Inside modules, our main concern is to guard against programming errors that are beyond the compiler’s static checks and go undetected at runtime until they blow things up non-locally in time or space. A reasonable coding style is to make assumptions explicit using types or assertions, allowing us to fail fast and cleanly, e.g. with an exception. Details vary by programming language of course. Examples: + +* If a value received over a platform channel is expected to have a certain type, immediately assign it to a variable of that type. + +* If a value received over a platform channel is expected to be non-null, either set things up to have it dereferenced immediately, or assert that it is non-null before storing it for later. Depending on your programming language, you may be able to assign it to a variable of a non-nullable type instead. + +Two simple examples: + +``` +*// Dart: we expect to receive a non-null List of integers.* +**for (final** int n **in** **await** channel.invokeMethod('getFib', 100)) { + print(n * n); +} + +// Android: we expect non-null name and age arguments for +// asynchronous processing, delivered in a string-keyed map. +channel.setMethodCallHandler { call, result -> + when (call.method) { + "bar" -> { + val name : String = call.argument("name") + val age : Int = call.argument("age") + process(name, age, result) + } + else -> result.notImplemented() + } +} +: +fun process(name: String, age: Int, result: Result) { ... } +``` + + +The Android code exploits the generically typed `<T> T argument(String key)` method of `MethodCall` which looks up the key in the arguments, assumed to be a map, and casts the value found to the target (call site) type. A suitable exception is thrown, if this fails for any reason. Being thrown from a method call handler, it would be logged, and an error result sent to the Dart side. + +### Don’t mock platform channels + +(Pun intended.) When writing unit tests for Dart code that uses platform channels, a knee jerk reaction may be to mock the channel object, as you would a network connection. + +You can certainly do that, but channel objects don’t actually need to be mocked to play nicely with unit tests. Instead, you can register mock message or method handlers to play the role of the platform during a particular test. Here is a unit test of a function `hello` that is supposed to invoke the `bar` method on channel `foo`: + +``` +test('gets greeting from platform', () **async** { + **const** channel = MethodChannel('foo'); + channel.setMockMethodCallHandler((MethodCall call) **async** { + **if** (call.method == 'bar') + **return** 'Hello, ${call.arguments}'; + **throw** MissingPluginException(); + }); + expect(**await** hello('world'), 'Platform says: Hello, world'); +}); +``` + + +To test code that sets up message or method handlers, you can synthesize incoming messages using `BinaryMessages.handlePlatformMessage`. At present, this method is not mirrored on platform channels, though that could easily be done as indicated in the code below. The code defines a unit test of a class `Hello` that is supposed to collect incoming arguments of calls to method `bar` on channel `foo`, while returning greetings: + +``` +test('collects incoming arguments', () **async** { + **const** channel = MethodChannel('foo'); + **final** hello = Hello(); + **final** String result = **await** handleMockCall( + channel, + MethodCall('bar', 'world'), + ); + expect(result, contains('Hello, world')); + expect(hello.collectedArguments, contains('world')); +}); + +// Could be made an instance method on class MethodChannel. +Future<dynamic> handleMockCall( + MethodChannel channel, + MethodCall call, +) async { + dynamic result; + await BinaryMessages.handlePlatformMessage( + channel.name, + channel.codec.encodeMethodCall(call), + (ByteData reply) { + if (reply == null) + throw MissingPluginException(); + result = channel.codec.decodeEnvelope(reply); + }, + ); + return result; +} +``` + + +Both examples above declare the channel object in the unit test. This works fine — unless you worry about the duplicated channel name and codec — because all channel objects with the same name and codec are equivalent. You can avoid the duplication by declaring the channel as a **`const`** somewhere visible to both your production code and the test. + +What you don’t need is to provide a way to inject a mock channel into your production code. + +### Consider automated testing for your platform interaction + +Platform channels are simple enough, but getting everything working from your Flutter UI via a custom Dart API backed by a separate Java/Kotlin and Objective-C/Swift implementation does takes some care. And keeping the setup working as changes are made to your app will, in practice, require automated testing to guard against regressions. This cannot be accomplished with unit testing alone because you need a real app running for platform channels to actually talk to the platform. + +Flutter comes with the `flutter_driver` integration test framework that allows you to test Flutter applications running on real devices and emulators. But `flutter_driver` is not currently integrated with other frameworks to enable testing across Flutter and platform components. I am confident this is one area where Flutter will improve in the future. + +In some situations, you can use `flutter_driver` as is to test your platform channel usage. This requires that your Flutter user interface can be used to trigger any platform interaction and that it is then updated with sufficient detail to allow your test to ascertain the outcome of the interaction. + +If you are not in that situation, or if you are packaging up your platform channel usage as a Flutter plugin for which you want a module test, you can instead write a simple Flutter app for testing purposes. That app should have the characteristics above and can then be exercised using `flutter_driver`. You’ll find an example in [the Flutter GitHub repo](https://github.com/flutter/flutter/tree/master/dev/integration_tests/platform_interaction). + +### Keep platform side ready for incoming synchronous calls + +Platform channels are asynchronous only. But there are quite a few platform APIs out there that make synchronous calls into your host app components, asking for information or help or offering a window of opportunity. One example is `Activity.onSaveInstanceState` on Android. Being synchronous means everything must be done before the incoming call returns. Now, you might like to include information from the Dart side in such processing, but it is too late to start sending out asynchronous messages once the synchronous call is already active on the main UI thread. + +The approach used by Flutter, most notably for semantics/accessibility information, is to proactively send updated (or updates to) information to the platform side whenever the information changes on the Dart side. Then, when the synchronous call arrives, the information from the Dart side is already present and available to platform side code. + +## Resources + +[Flutter API documentation](https://docs.flutter.io/): + +* DartDoc for the [services library](https://docs.flutter.io/flutter/services/services-library.html) which contains the Dart platform channel types. + +* JavaDoc for the [io.flutter.plugins.common package](https://docs.flutter.io/javadoc/io/flutter/plugin/common/package-summary.html) which contains the Android platform channel types. + +* ObjcDoc for the [iOS Flutter library](https://docs.flutter.io/objcdoc/Classes.html). + +Guides: + +* The [flutter.io website](https://flutter.io) documents [how to use method channels](https://flutter.io/platform-channels/) and the Dart/Android/iOS value conversions involved in using the standard method codec. + +* The *Boring Flutter Development Show*, Episode 6: [Packages and plugins](https://youtu.be/ht2bDlJd2c4) is a YouTube video showing a Flutter plugin being implemented, live, using platform channels. + +Code examples: + +* The [flutter/flutter GitHub repo](https://github.com/flutter/flutter) contains [a basic example](https://github.com/flutter/flutter/tree/master/examples/platform_channel) of using method and event channels to access device battery information, wrapped up in a complete Flutter app. + +* The [flutter/plugins GitHub repo](https://github.com/flutter/plugins) contains several examples of using platform channels to implement Flutter plugins. The code is located in the [packages](https://github.com/flutter/plugins/tree/master/packages) sub-folder, organized by plugin. Each plugin includes a complete example app. \ No newline at end of file diff --git a/sites/www/content/blog/flutter-q2-survey/images/1BVNfy8-YPsP5jHKuD3P-Cg.gif b/sites/www/content/blog/flutter-q2-survey/images/1BVNfy8-YPsP5jHKuD3P-Cg.gif new file mode 100644 index 0000000000..b2ccf12bfe Binary files /dev/null and b/sites/www/content/blog/flutter-q2-survey/images/1BVNfy8-YPsP5jHKuD3P-Cg.gif differ diff --git a/sites/www/content/blog/flutter-q2-survey/index.md b/sites/www/content/blog/flutter-q2-survey/index.md new file mode 100644 index 0000000000..6db453c18e --- /dev/null +++ b/sites/www/content/blog/flutter-q2-survey/index.md @@ -0,0 +1,20 @@ +--- +title: "Flutter Q2 survey" +description: "Just a reminder! Please take the Flutter Q2 survey! The survey closes at the end of day on Friday, June 19th." +publishDate: 2026-06-15 +author: sfshaza2 +image: images/1BVNfy8-YPsP5jHKuD3P-Cg.gif +category: announcements +layout: blog +--- + +<DashImage figure src="images/1BVNfy8-YPsP5jHKuD3P-Cg.gif" alt="Dash completes the Q2 Flutter survey!" caption="Dash completes the Q2 Flutter survey!" /> + + +## Flutter Q2 survey + +Just a reminder! Please take the [Flutter Q2 survey](https://google.qualtrics.com/jfe/form/SV_3drKjSfjNeLZfq6?Source=Website)! The survey closes at the end of day on Friday, June 19th. + +It won’t take long and your feedback helps drive our roadmap! + +Thank you! \ No newline at end of file diff --git a/sites/www/content/blog/flutter-spring-2020-update/images/0T39T7UB0oJVIQy_H.webp b/sites/www/content/blog/flutter-spring-2020-update/images/0T39T7UB0oJVIQy_H.webp new file mode 100644 index 0000000000..f058e0dd00 Binary files /dev/null and b/sites/www/content/blog/flutter-spring-2020-update/images/0T39T7UB0oJVIQy_H.webp differ diff --git a/sites/www/content/blog/flutter-spring-2020-update/images/0vX4VeEgvAD364f_2.webp b/sites/www/content/blog/flutter-spring-2020-update/images/0vX4VeEgvAD364f_2.webp new file mode 100644 index 0000000000..496e75705e Binary files /dev/null and b/sites/www/content/blog/flutter-spring-2020-update/images/0vX4VeEgvAD364f_2.webp differ diff --git a/sites/www/content/blog/flutter-spring-2020-update/index.md b/sites/www/content/blog/flutter-spring-2020-update/index.md new file mode 100644 index 0000000000..1690954045 --- /dev/null +++ b/sites/www/content/blog/flutter-spring-2020-update/index.md @@ -0,0 +1,118 @@ +--- +title: "Flutter Spring 2020 Update" +description: "Continued momentum and enterprise growth, new release model" +publishDate: 2020-04-22 +author: timsneath +image: images/0T39T7UB0oJVIQy_H.webp +category: announcements +layout: blog +--- + +*Authored by Tim Sneath and Patrick Sosinski* + +For the engineers, product managers, UX researchers, technical writers, and developer relations engineers here at Google that work on Flutter, the last couple of months have been filled with various challenges; like most of you, we are all grappling with very different daily routines than normal. And yet, as an open source project, we are able to continue to develop using publicly available tools. Within the limitations of sometimes challenging and certainly novel working environments, along with the need to care for dependents, we’re continuing to make progress against our spring backlog and have also learned plenty about contributing “from the outside”! We hope that you’re also able to make progress and stay healthy and safe. + +<DashImage figure src="images/0T39T7UB0oJVIQy_H.webp" /> + + +## Flutter momentum + +We continue to see fast growth in Flutter usage, with over **two million developers having used Flutter** in the sixteen months since we released. Despite these unprecedented circumstances, in March we saw 10% month-over-month growth, with nearly half a million developers now using Flutter each month. + +Some other interesting statistics: + +* 60% of you are developing with Windows, 27% are using macOS, and 13% are using Linux. + +* 35% of you work for a startup, 26% are enterprise developers, 19% are self-employed, and 7% work for design agencies. + +* 78% of Flutter developers use the stable channel, 11% use beta, and 11% use either dev or master. + +* The top five territories for Flutter are India, China, the United States, the EU, and Brazil. + +* There are approximately 50,000 Flutter apps published in the Play Store, with nearly 10,000 uploaded in the last month alone. + +* The most popular framework packages used in Flutter apps are [http](https://pub.dev/packages/http), [shared_preferences](https://pub.dev/packages/shared_preferences), [intl](https://pub.dev/packages/intl), [meta](https://pub.dev/packages/meta), [path_provider](https://pub.dev/packages/path_provider) and [pedantic](https://pub.dev/packages/pedantic). + +* The most popular third-party packages used in Flutter apps are [provider](https://pub.dev/packages/provider), [rxdart](https://pub.dev/packages/rxdart), [cached_network_image](https://pub.dev/packages/cached_network_image), [sqflite](https://pub.dev/packages/sqflite), [font_awesome_flutter](https://pub.dev/packages/font_awesome_flutter) and [flutter_launcher_icons](https://pub.dev/packages/flutter_launcher_icons). + +## Flutter in the enterprise + +Flutter usage is growing fast among enterprise customers in particular, and our research continues to show that the ability to build highly-branded experiences that support multiple platforms is a key reason why larger companies select Flutter. One recent example is [Nubank](https://nubank.com.br/en/), the largest digital bank outside Asia with over 20 million customers. After conducting [a detailed investigation and analysis](https://cdn.nubank.com.br/mobile/taskforce/nubank-mobile-architecture-task-force-mission-report.pdf) of their choices for app development, Nubank selected Flutter and have since been able to unify their front-end development team on a single framework, enabling them to ship new features simultaneously on both iOS and Android. + +Check out their developer story below, which explains some of the benefits they’re seeing with Flutter: + +<YoutubeEmbed id="vnj2i6RNo3g" title="Nubank (Flutter Developer Stories)" fullwidth="true"/> + + +One common request from enterprises is professional components. We’re partnering with [SyncFusion](https://www.syncfusion.com/), whose Essential Studio product now includes a range of [high-quality Flutter components](https://www.syncfusion.com/flutter-widgets) including charting, PDF manipulation and barcode generation. With their [2020.1 release](https://www.businesswire.com/news/home/20200421005174/en/Syncfusion-Introduces-New-Flutter-Widgets-Web-Compatibility), all components support Android, iOS *and* web out of the box, and they now have a [web-based preview](https://flutter.syncfusion.com/#/) of their controls. + +## Updating our release process + +Lastly, as we look towards our next stable release, we wanted to share some changes we’re making to our release model that we think will further improve stability and predictability of our releases. + +Today’s release process was designed to be simple and low maintenance. This process served Flutter well when we were a smaller team and a more nascent framework, but at our current scale, we have encountered some issues affecting Flutter contributors and developers including: + +* lack of clarity on when the release would be built, and therefore what code was in it + +* lack of testing for branches causing hotfix releases to have regressions + +Starting with the April release of Flutter, we are moving to a branching model with a stabilization period for beta and stable releases. We will now branch at the beginning of the month for a beta release and stabilize this release by cherry-picking any critical fixes. Roughly once a quarter, the current beta branch will be promoted to our stable release. We will continue to do hotfixes on this release if necessary. Our infrastructure now supports testing against branches, meaning we can validate cherrypicks and will accept some requests based on severity. + +We also took this opportunity to align the Flutter and Dart release processes and channels. As a result, Dart has added [a beta channel](https://dart.dev/get-dart#release-channels), and moving forward releases will be synced (for example, Flutter beta releases will contain a Dart beta release). + +If you’re already shipping a Flutter app based on the stable channel, we encourage you to test your app on beta candidate releases and influence the quality of the stable release by reporting any issues. You can also escalate regressions or blocking bugs on the stable channel following the new [Flutter cherry-pick process](https://github.com/flutter/flutter/wiki/Flutter-Cherrypick-Process) on the Flutter wiki. + +We think this new process will both provide higher confidence in the quality and predictability of our releases, and an easier way to deliver hotfixes to the stable channel. + +## Versioning changes + +As a part of this branching model, we are making some minor changes to the way releases are versioned. + +The full technical details can be found at the [Flutter build release channels](https://github.com/flutter/flutter/wiki/Flutter-build-release-channels) wiki page; a quick summary follows: + +*Non-stable release versions* will be annotated with `.pre` in the version string to denote their pre-release status. Given a version string of `x.y.z-n.m.pre`, `dev` channel builds will increment `n` each time a new one is built from `master`. + +* `1.18.0–1.0.pre`: first `dev` build after `master` moves to `1.18` + +* `1.18.0–2.0.pre`: next `dev` build from a more recent point on `master` + +*Beta releases* will be built from a `dev` release point, as above. When we do cherry-picks on one of these releases, the `m` version will increment. For example, if we took the 15th `dev` build from `master` as our `1.18` release from `beta`, the versioning would look like: + +* `1.18.0–15.0.pre`: initial `beta` candidate (same release that went to `dev`) + +* `1.18.0–15.1.pre`: subsequent build on the (now)`beta` branch with some cherry-picks + +* `1.18.0–15.2.pre`: second subsequent build + +*Stable releases* will be versioned `x.y.0`. Subsequent hotfix releases, if necessary, will increment the patch number. `x.y.1`, `x.y.2`, etc.) + +* `1.18.0–15.4.pre`: last beta build on branch + +* `1.18.0`: stable release, same bits as `1.18.0–15.4-pre` + +* `1.18.1`: potential hotfix of `1.18.0` + +## What’s next? + +Our first release using this new versioning model will be **our next stable release, which we plan to ship next week**. Come back then for a full rundown of all the new features. + +In the meantime, check out some of the other announcements we’ve made over the last couple of weeks. Last week, we announced [CodePen support of Flutter](https://medium.com/flutter/announcing-codepen-support-for-flutter-bb346406fe50). We are enjoying seeing the pens that various creators have built over the last few days. Here are a few of our favorites: + +* [Twitter clone](https://codepen.io/mkiisoft/pen/KKdgdad) (mkiisoft) + +* [Generative abstract art](https://codepen.io/rx-labz/pen/WNQoNem) (rxlabz) + +* [Chakra animation](https://codepen.io/tahatesser/pen/GRpqbRY) (tahatesser) + +* [Rotating carousel](https://codepen.io/joshuadeguzman/pen/jObrzJB) (joshuadeguzman) + +* [Nougat animation](https://codepen.io/phillywiggins/pen/gOaPNPY) (phillywiggins) + +* [Double pendulum](https://codepen.io/abhilas-csc/pen/qBOZKPj) (abhilas-csc) + +<DashImage figure src="images/0vX4VeEgvAD364f_2.webp" /> + + +And if you’re looking for Flutter learning resources, we now have [a free introductory Flutter training course](https://medium.com/flutter/learn-flutter-for-free-c9bc3b898c4d) available online. This ten hour course presented by Angela Yu provides tutorials and labs to help you get started on your Flutter journey. + +See you next week. In the meantime, stay home and stay healthy! \ No newline at end of file diff --git a/sites/www/content/blog/flutter-takes-the-stage-at-gdc/images/0zVBJt_90TX8KEYoD.webp b/sites/www/content/blog/flutter-takes-the-stage-at-gdc/images/0zVBJt_90TX8KEYoD.webp new file mode 100644 index 0000000000..55cfdfd5d8 Binary files /dev/null and b/sites/www/content/blog/flutter-takes-the-stage-at-gdc/images/0zVBJt_90TX8KEYoD.webp differ diff --git a/sites/www/content/blog/flutter-takes-the-stage-at-gdc/index.md b/sites/www/content/blog/flutter-takes-the-stage-at-gdc/index.md new file mode 100644 index 0000000000..bf9d977acf --- /dev/null +++ b/sites/www/content/blog/flutter-takes-the-stage-at-gdc/index.md @@ -0,0 +1,58 @@ +--- +title: "Flutter takes the stage at GDC" +description: "Last year saw a major update to the Flutter Casual Games toolkit. This week, Flutter made its debut at the Game Developers Conference (GDC)…" +publishDate: 2024-03-20 +author: zoeyfan +image: images/0zVBJt_90TX8KEYoD.webp +category: events +layout: blog +--- + +Last year saw a [major update to the Flutter Casual Games toolkit](https://medium.com/flutter/building-your-next-casual-game-with-flutter-716ef457e440). This week, Flutter made its [debut](https://schedule.gdconf.com/session/google-developer-summit-building-multiplatform-games-with-flutter-presented-by-google/903077) at the [Game Developers Conference](https://gdconf.com/) (GDC) in San Francisco, the game industry’s premier professional event, championing game developers and the advancement of their craft. Read on to see progress developers have made building games on Flutter, and what’s in store for the future of Flutter game development. + +<DashImage figure src="images/0zVBJt_90TX8KEYoD.webp" alt="Flutter for Games hero image" caption="Flutter for Games hero image" /> + + +Since the launch of the [Flutter Casual Games Toolkit](http://flutter.dev/games), Flutter has made strong progress within the game development arena. More than 15,000 new mobile games have been developed with Flutter in the last 12 months. + +Flutter has proven to be an appealing option for game apps of all sizes, empowering both first-time game devs like [Tatsuya](https://github.com/tty215), who used Flutter to create [Tinies Merge](https://play.google.com/store/apps/details?id=com.ttydev.tiniesmerge) in his spare time and secured a spot as a top 20 finalist at [Google Play’s Indie Game Festival](https://android-developers-jp.googleblog.com/2023/07/IndieGamesFestival2023.html), to the teams behind popular titles such as [Trivia Crack](https://play.google.com/store/apps/details?id=com.etermax.preguntados.lite&hl=en_US&gl=US), which has hundreds of millions of downloads across the globe! + +## Why Flutter for Games? + +Flutter offers compelling advantages for 2D casual games, particularly interface-driven or turn-based titles. The fact that it’s open-source eliminates licensing fees, provides deep control over game mechanics, and grants access to a rich plugin ecosystem. Flutter’s “hot reload” feature accelerates development, while its optimized engine ensures smooth performance across devices, including browsers. With the ability to build once and deploy cross-platform, including iOS, Android, web, desktop, and [Google Play Games for PC](https://play.google.com/googleplaygames), Flutter streamlines your game’s reach. So, if you’re a game developer, don’t overlook Flutter’s potential — try Flutter for your next game and help us spread the word! + +## 2024 and beyond: building the Flutter Game Development ecosystem together + +In 2024, we’re dedicated to empowering you to create amazing casual game experiences with Flutter. We’ll focus on: + +* **Streamlining development:** Expect expanded game development resources like templates, plugin-integrations, and in-depth guides to speed up your workflow and unlock Flutter’s game development potential. + +* **Expanding your reach:** We’re working to make deploying Flutter games on the web and other platforms even smoother, opening up new audiences for your creations. + +* **Foundations for the future:** While 2D mobile game support is our main focus, we are keen to explore 3D capabilities as a part of [Flutter GPU](https://github.com/flutter/flutter/wiki/Flutter-GPU) effort. Follow some of our [early experiments](https://github.com/bdero/flutter-scene-example/tree/gdc2024) to see our progress. + +## A special thank you to the community + +We are incredibly grateful to the community for your unwavering support and significant contributions, which have fueled Flutter’s remarkable growth in the game development space. + +We are in awe of your work, and incredibly thankful for the many brilliant contributions, including: + +* [Luan Nico](https://github.com/luanpotter), [Lukas Klingsbo](https://github.com/spydon), [Erick Zanardo](https://github.com/erickzanardo), [Renan Araujo](https://github.com/renancaraujo), [Arron Washington](https://github.com/radicaled), [Pasha Stetsenko](https://github.com/st-pasha), [Jochum van der Ploeg](https://github.com/wolfenrain), [Fernando Ultremare](https://github.com/feroult), [DevKage](https://github.com/ufrshubham) ([Flame](https://flame-engine.org/)): Your work on the Flame engine and its related projects has been transformative for Flutter game development. + +* [Rafael Barbosa](https://github.com/RafaelBarbosatec) ([Bonfire](https://bonfire-engine.github.io/#/)): Your powerful and versatile game engine is a fantastic tool for building RPG-style games. + +* [Filip ](https://github.com/filiph)Hráček: Your templates and tutorials provide an invaluable starting point for many new game developers. + +* [Jeff Ward](https://github.com/subhash279): Embedding Dart in other game engines opens up exciting possibilities. + +* The amazing team at @VeryGoodVentures: Your beautiful games and willingness to share the best practices raise the bar for everyone. + +* [Abedalkareem Omreyh](https://github.com/Abedalkareem) ([game services](https://pub.dev/packages/games_services)), [August](https://github.com/Gustl22), [subhash279](https://github.com/subhash279) ([audioplayers](https://pub.dev/packages/audioplayers)), [Marco Bavagnoli](https://github.com/alnitak) ([Flutter SoLoud](https://pub.dev/packages/flutter_soloud)) and **countless others** who create essential packages: The foundation you provide is what makes complex and engaging games possible with Flutter. + +This list barely scratches the surface of the talent in this community. To everyone we couldn’t name individually: your contributions are deeply appreciated, and you make Flutter a vibrant place for game development! + +Meanwhile, we would also like to extend our deepest thanks to our developers who have launched games using Flutter. Your creativity and passion inspire us to continue pushing the boundaries of what’s possible, making Flutter an even more powerful tool for developers worldwide. + +Flutter’s journey as a game development platform is just beginning. We’re committed to growing alongside the community, listening to your feedback, and building the tools you need. + +We can’t wait to see what you build! \ No newline at end of file diff --git a/sites/www/content/blog/flutter-web-navigating-urls-using-named-routes/images/0Uoo0IGsUFUf8GJsT.webp b/sites/www/content/blog/flutter-web-navigating-urls-using-named-routes/images/0Uoo0IGsUFUf8GJsT.webp new file mode 100644 index 0000000000..32a4a601ce Binary files /dev/null and b/sites/www/content/blog/flutter-web-navigating-urls-using-named-routes/images/0Uoo0IGsUFUf8GJsT.webp differ diff --git a/sites/www/content/blog/flutter-web-navigating-urls-using-named-routes/images/0kDK1lFIWHzjcEVKA.webp b/sites/www/content/blog/flutter-web-navigating-urls-using-named-routes/images/0kDK1lFIWHzjcEVKA.webp new file mode 100644 index 0000000000..4dad1af7a7 Binary files /dev/null and b/sites/www/content/blog/flutter-web-navigating-urls-using-named-routes/images/0kDK1lFIWHzjcEVKA.webp differ diff --git a/sites/www/content/blog/flutter-web-navigating-urls-using-named-routes/index.md b/sites/www/content/blog/flutter-web-navigating-urls-using-named-routes/index.md new file mode 100644 index 0000000000..169f021ca3 --- /dev/null +++ b/sites/www/content/blog/flutter-web-navigating-urls-using-named-routes/index.md @@ -0,0 +1,143 @@ +--- +title: "Flutter web: Navigating URLs using named routes" +description: "Named routes can be used to navigate between pages inside a Flutter mobile app, but they also work for URLs in Flutter web apps. This…" +publishDate: 2020-03-30 +author: perclasson +image: images/0Uoo0IGsUFUf8GJsT.webp +category: announcements +layout: blog +--- + +👉 Update: [Flutter’s new Navigation and Routing system](https://medium.com/flutter/learning-flutters-new-navigation-and-routing-system-7c9068155ade) is now available, and it is the preferred way to handle URLs in Flutter. + +Named routes can be used to navigate between pages inside a Flutter mobile app, but they also work for URLs in Flutter web apps. This article explains how to add named routes to your app, and how you can customize them, to have pattern matching inside the routes. + +## Defining named routes + +Named routes can be added to your application by defining them within the `MaterialApp` class. The `MaterialApp.routes` property contains a map listing each named route and its associated display widget. The `MaterialApp.initialRoute` property determines what route displays as the application starts. The `initialRoute` thus needs to be defined inside of the `routes` property. For example: + +```dart +MaterialApp( + initialRoute: '/', + routes: { + '/': (context) => HomePage(), + '/overview': (context) => OverviewPage(), + }, +); +``` + +A good practice to keep your code organized is to put named routes inside static variables, for example, on the widgets themselves: + +```dart +class OverviewPage extends StatelessWidget { + static const String route = '/overview'; +} +``` + +Next, refactor `MaterialApp.routes` with the named routes now defined as static variables: + +```dart +MaterialApp( + initialRoute: HomePage.route, + routes: { + HomePage.route: (context) => HomePage(), + OverviewPage.route: (context) => OverviewPage(), + }, +); +``` + +## Navigating between pages + +To navigate from one page to another, simply push the named route to the navigator: + +```dart +RaisedButton( + onPressed: () { + Navigator.of(context).pushNamed(OverviewPage.route); + }, +) +``` + +See a [full interactive example of this on DartPad](https://dartpad.dev/embed-flutter.html?gh_owner=perclasson&gh_repo=flutter_code&gh_path=named_routes/lib&theme=dark). If you build and [run the application yourself for the web](https://flutter.dev/docs/get-started/web), you can also just type in `/#/overview` inside the web browser. This pushes the route named *overview* to the `Navigator`, and takes you to the `OverviewPage` widget, as you can see in the following GIF: + +<DashImage figure src="images/0kDK1lFIWHzjcEVKA.webp" alt="[See full interactive example on DartPad.](https://dartpad.dev/embed-flutter.html?gh_owner=perclasson&gh_repo=flutter_code&gh_path=named_routes/lib&theme=dark)" caption="[See full interactive example on DartPad.](https://dartpad.dev/embed-flutter.html?gh_owner=perclasson&gh_repo=flutter_code&gh_path=named_routes/lib&theme=dark)" /> + + +## Routing logic for dynamic URLs + +You may need to address more complex scenarios then addressed here, such as having pattern matching inside the routes to allow for dynamic URLs. To extend this example, assume that you have many different articles on the overview page. For each article you want to be able to navigate directly via URLs: + +``` +/#/article/a-very-interesting-article +/#/article/newsworthy-news +``` + + +To define named routes for all of the articles within the `MaterialApp` does not scale very well. For such dynamic cases you need to do something more custom. As of this writing, the stable channel of Flutter is at v1.12, and there is no *simple way* to do this, though there are plans to add support for more advanced routing with the [new Navigator](https://github.com/flutter/flutter/issues/45938). + +For now you can use an external package, for example the [Fluro package](https://pub.dev/packages/fluro/) provides more advanced routing. It gives you wildcard pattern matching in routes, as well as parsing for query strings in URLs. There are probably many other available packages, so please leave a comment with the name of your favorite. + +If you are up for the challenge, you can also get dynamic routes by making use of the `MaterialApp.onGenerateRoute` property. Use this to write routing logic for when a named route is not inside of `MaterialApp.routes`. + +For each route, define a `Path` with a `RegEx` pattern. If the named route matches the pattern, return the associated widget. Next, define the `Path` class to support that: + +```dart +class Path { + const Path(this.pattern, this.builder); + + final String pattern; + final Widget Function(BuildContext, String) builder; +} +``` + +For the overview page and the home route, it is quite simple and looks similar to what you had before. The following example creates a `RegEx` pattern that matches [a slug](https://en.wikipedia.org/wiki/Clean_URL#Slug) (lowercase letters with a dash) that is used to find the corresponding article: + +```dart +static List<Path> paths = [ + Path( + r'^/article/([\w-]+)$', + (context, match) => Article.getArticlePage(match), + ), + Path( + r'^' + OverviewPage.route, + (context, match) => OverviewPage(), + ), + Path( + r'^' + HomePage.route, + (context, match) => HomePage(), + ), +]; +``` + +All that remains is to create an `onGenerateRoute` function for `MaterialApp`. If the current named route (`settings.name`) is defined in the paths list, return the associated widget. Make sure to pass any named matches in the `RegEx` (in this example it is the slug). If no matches are found, simply return null, `WidgetsApp.onUnknownRoute` is called to handle such cases: + +```dart +static Route<dynamic> onGenerateRoute(RouteSettings settings) { + for (Path path in paths) { + final regExpPattern = RegExp(path.pattern); + if (regExpPattern.hasMatch(settings.name)) { + final firstMatch = regExpPattern.firstMatch(settings.name); + final match = (firstMatch.groupCount == 1) ? firstMatch.group(1) : null; + return MaterialPageRoute<void>( + builder: (context) => path.builder(context, match), + settings: settings, + ); + } + } + // If no match is found, [WidgetsApp.onUnknownRoute] handles it. + return null; +} +``` + +Make sure to define the `onGenerateRoute` function inside the `MaterialApp` class; you have implemented dynamic URLs with Flutter, using named routes! You can [see a full interactive example on DartPad](https://dartpad.dev/embed-flutter.html?gh_owner=perclasson&gh_repo=flutter_code&gh_path=custom_named_routes/lib&theme=dark). + +<DashImage figure src="images/0Uoo0IGsUFUf8GJsT.webp" alt="[See full interactive example on DartPad.](https://dartpad.dev/embed-flutter.html?gh_owner=perclasson&gh_repo=flutter_code&gh_path=custom_named_routes/lib&theme=dark)" caption="[See full interactive example on DartPad.](https://dartpad.dev/embed-flutter.html?gh_owner=perclasson&gh_repo=flutter_code&gh_path=custom_named_routes/lib&theme=dark)" /> + + +## Conclusion + +Whether you choose to write your own custom logic for routes, or simply use the routes support that exists within `MaterialApp`, you get URL support by default with a Flutter app on the web, when using named routes. Implementing named routes can also ensure that you decouple the presentation logic from the routing logic, which leads to less code duplication. + +Let me know in the comments what solution you use in your app, whether it is writing your own custom logic or using an external package. + +Happy hacking! \ No newline at end of file diff --git a/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/06XtFgsx4cz1OY88N.jpg b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/06XtFgsx4cz1OY88N.jpg new file mode 100644 index 0000000000..630ff6e730 Binary files /dev/null and b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/06XtFgsx4cz1OY88N.jpg differ diff --git a/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0O9LlG-FDFRIFAsX0.webp b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0O9LlG-FDFRIFAsX0.webp new file mode 100644 index 0000000000..590b0fdc1f Binary files /dev/null and b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0O9LlG-FDFRIFAsX0.webp differ diff --git a/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0PHaTJqBxdvTDycOh.webp b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0PHaTJqBxdvTDycOh.webp new file mode 100644 index 0000000000..be1b8e4492 Binary files /dev/null and b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0PHaTJqBxdvTDycOh.webp differ diff --git a/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0W5BGuCaX7v9rvtjh.webp b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0W5BGuCaX7v9rvtjh.webp new file mode 100644 index 0000000000..a09c9ecf81 Binary files /dev/null and b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0W5BGuCaX7v9rvtjh.webp differ diff --git a/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0ai8xTn_dMUTB6af6.webp b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0ai8xTn_dMUTB6af6.webp new file mode 100644 index 0000000000..43a870422d Binary files /dev/null and b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0ai8xTn_dMUTB6af6.webp differ diff --git a/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0bf3e0LQ8MVQb0ETI.webp b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0bf3e0LQ8MVQb0ETI.webp new file mode 100644 index 0000000000..899a930762 Binary files /dev/null and b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0bf3e0LQ8MVQb0ETI.webp differ diff --git a/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0eLfOUS1mLRtcDr4j.webp b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0eLfOUS1mLRtcDr4j.webp new file mode 100644 index 0000000000..ad51d9526b Binary files /dev/null and b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0eLfOUS1mLRtcDr4j.webp differ diff --git a/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0kS0WlZkECsmnqaY7.webp b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0kS0WlZkECsmnqaY7.webp new file mode 100644 index 0000000000..8a05e57ad0 Binary files /dev/null and b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0kS0WlZkECsmnqaY7.webp differ diff --git a/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0yDQInopKQv6QR0Hb.webp b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0yDQInopKQv6QR0Hb.webp new file mode 100644 index 0000000000..cdf1dc0974 Binary files /dev/null and b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/0yDQInopKQv6QR0Hb.webp differ diff --git a/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/1bbkcrsggiQLxNDRAgHiSBQ.webp b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/1bbkcrsggiQLxNDRAgHiSBQ.webp new file mode 100644 index 0000000000..21ea8a3fb9 Binary files /dev/null and b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/1bbkcrsggiQLxNDRAgHiSBQ.webp differ diff --git a/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/1eq4uks7XD1mMWrTQVTUY6w.webp b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/1eq4uks7XD1mMWrTQVTUY6w.webp new file mode 100644 index 0000000000..49d09a6be7 Binary files /dev/null and b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/images/1eq4uks7XD1mMWrTQVTUY6w.webp differ diff --git a/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/index.md b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/index.md new file mode 100644 index 0000000000..231c85be16 --- /dev/null +++ b/sites/www/content/blog/flutter-web-support-hits-the-stable-milestone/index.md @@ -0,0 +1,174 @@ +--- +title: "Flutter web support hits the stable milestone" +description: "Publish to web and mobile from the same codebase" +publishDate: 2021-03-03 +author: mariam_hasnany +image: images/1bbkcrsggiQLxNDRAgHiSBQ.webp +category: announcements +layout: blog +--- + +<DashImage figure src="images/1bbkcrsggiQLxNDRAgHiSBQ.webp" /> + + +Our vision for Flutter is to be a portable UI framework for building beautiful app experiences on any platform. Today, as part of [Flutter 2](https://medium.com/flutter/whats-new-in-flutter-2-0-fe8e95ecc65), we’re announcing that Flutter’s web support has hit the stable milestone. + +The first Flutter release supported iOS and Android, and has been used to bring over 150,000 apps to mobile app stores. Now, adding web support means that those same apps can reach an even broader audience, as well as opening up new ways to build interactive experiences on the web. + +In this initial release of web support, we’re focusing on three app scenarios: + +* **Progressive web apps (PWAs)** that combine the web’s reach with the capabilities of a desktop app. + +* **Single page apps (SPAs)** that load once and transmit data to and from internet services. + +* **Expanding existing Flutter mobile apps to the web**, enabling shared code for both experiences. + +This article describes what we’ve built so far, and explores examples of how you can take advantage of Flutter’s web support in your own apps. + +<DashImage figure src="images/0yDQInopKQv6QR0Hb.webp" alt="[iRobot Education](https://edu.irobot.com/the-latest/building-a-coding-experience-for-all) developed the [iRobot Coding app](https://code.irobot.com/) using Flutter to offer their learn-to code experience to everyone anywhere by making it available on the web." caption="[iRobot Education](https://edu.irobot.com/the-latest/building-a-coding-experience-for-all) developed the [iRobot Coding app](https://code.irobot.com/) using Flutter to offer their learn-to code experience to everyone anywhere by making it available on the web." /> + + +## Our journey to the web + +Today’s web platform is richer than ever, with [hardware-accelerated 2D and 3D graphics](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API), [offline and installation support](https://web.dev/progressive-web-apps/), and [access to the underlying operating system and hardware](https://web.dev/fugu-status/). The web has allowed a [broad](https://vuejs.org/) [array](https://angular.io/) [of](https://flask.palletsprojects.com/) [frameworks](https://reactjs.org/) that build on this underlying platform to offer developers great flexibility in how they create apps for the web. + +Since Flutter is written in [Dart](https://dart.dev/), a language that offers JavaScript compilation, it was a natural next step to explore the web as a target. This also builds towards our vision of providing a portable framework for building beautiful UI wherever you want to paint pixels. + +Our approach is to build a consistent toolkit that works across all platforms (rather than have two separate frameworks that have subtle deviations of behavior), to ensure that your own code runs without surprises. + +<DashImage figure src="images/0ai8xTn_dMUTB6af6.webp" /> + + +At an architectural level, Flutter is a [multilayer system](https://flutter.dev/docs/resources/technical-overview#layer-cakes-are-delicious) with: + +* A **framework** that provides abstractions for common idioms like widgets, animation, and gestures. + +* An **engine** that renders to the target device using the system APIs it exposes. + +The framework itself is written in Dart, and the roughly 700,000 lines of core Flutter framework code are the same across all platforms: mobile, desktop, and now web. The same is true for your code; we use either the Dart development compiler **([`dartdevc`](https://dart.dev/tools/dartdevc)) or the Dart deployment compiler ([`dart2js`](https://dart.dev/tools/dart2js)) to compile your code into JavaScript, which can then be hosted on a server. + +With Dart’s ability to compile the Flutter framework (as well as your app code) into JavaScript, our work to support the web involved replacing the low-level C++ rendering engine used by mobile apps with code that maps to web platform APIs. Flutter doesn’t merely transpile to HTML equivalents of its widgets. Instead, Flutter’s web engine offers a choice of two renderers: an HTML renderer that is optimized for size and broad compatibility, and a CanvasKit renderer that uses WebAssembly and WebGL to render Skia paint commands to the browser canvas. + +Our goal for Flutter is to offer a new way to target the web platform, build on existing foundations, and provide new insights that improve the web for everyone. + +## Delivering a production-quality stable release + +Since our [beta release of web support](https://medium.com/flutter/web-support-for-flutter-goes-beta-35b64a1217c0) a year ago, we’ve learned a lot about how early adopters are using it, and we’ve worked with a few customers who have now shipped their Flutter web apps to production. + +Over this period, we have made major architectural improvements and added features that extend and optimize Flutter for the web, focusing on four areas: **performance**, **web-specific features**, **desktop form factors,** and **plugins.** + +<DashImage figure src="images/1eq4uks7XD1mMWrTQVTUY6w.webp" /> + + +### Performance + +The biggest area of improvement since our early releases is performance. During development, we’ve gained a better understanding of the performance and correctness characteristics of the various rendering technologies available on the web. + +We originally started with an HTML, DOM-based model. In this model, Flutter’s web engine translates each generated Flutter scene into HTML, CSS, or Canvas, and renders a frame onto the page as a tree of HTML elements. Even though the HTML renderer offered the greatest compatibility with a wide variety of browsers and had a smaller code size, the HTML renderer’s repaint performance was less suitable for more graphic-intensive applications like [Rive](https://rive.app/), a collaborative tool built with Flutter to create motion graphics. + +<DashImage figure src="images/0kS0WlZkECsmnqaY7.webp" alt="[Rive](https://rive.app/), a tool for creating custom animations, rebuilt their app using Flutter on the web and it is now available in beta." caption="[Rive](https://rive.app/), a tool for creating custom animations, rebuilt their app using Flutter on the web and it is now available in beta." /> + + +To handle the fidelity required to render intensive graphics efficiently, we started experimenting with [CanvasKit](https://skia.org/user/modules/canvaskit), which enables rendering Skia paint commands in the browser using [WebAssembly](https://webassembly.org/) and [WebGL](https://www.khronos.org/webgl/). We discovered that CanvasKit can deliver superior performance, fidelity, and correctness, enabling the level of graphical horsepower expressed in this [demo](https://flutterplasma.dev/) by [Felix Blaschke](https://github.com/felixblaschke), a talented member of the Flutter community. + +<DashImage figure src="images/0PHaTJqBxdvTDycOh.webp" alt="[Flutter Plasma](https://flutterplasma.dev/), a demo created by Felix Blaschke, running on Safari, Firefox, Edge and Chrome." caption="[Flutter Plasma](https://flutterplasma.dev/), a demo created by Felix Blaschke, running on Safari, Firefox, Edge and Chrome." /> + + +Each renderer offers advantages in different scenarios, so Flutter is supporting both rendering modes: + +* **HTML renderer:** Uses a combination of HTML elements, CSS, Canvas elements, and SVG elements. This renderer has a smaller download size. + +* **CanvasKit renderer:** This renderer is fully consistent with Flutter mobile and desktop, has faster performance with higher widget density, but adds about 2MB in download size. + +To optimize your Flutter web app for the characteristics of each device, the rendering mode is set by default to auto. This means that your app runs with the HTML renderer on mobile browsers and with CanvasKit on desktop browsers. + +You can also use `--web-renderer html` or `--web-renderer canvaskit` to explicitly choose which renderer your app uses. For more information, see [Web renderers](https://flutter.dev/docs/development/tools/web-renderers). + +### Web-specific features + +A Flutter app running in the browser should feel like a web app. So we added features to Flutter that help you use the best of the web. + +The web has many strengths, especially its global reach. One of the many reasons to bring your existing Flutter app to the web is to reach users outside of app stores. To do that, we added support for [custom URL strategies](https://flutter.dev/docs/development/ui/navigation/url-strategies), to ensure that it’s possible for your users to reach your app anywhere with just a click of a URL. With this feature, you have control over the URLs shown in the address bar as well as the routing of your app on the web. + +<DashImage figure src="images/0bf3e0LQ8MVQb0ETI.webp" alt="[Flutter Plasma demo](https://flutterplasma.dev/)’s showroom page is an example of the [url_strategy plugin](https://pub.dev/packages/url_strategy), based on Flutter’s custom URL strategies, in action." caption="[Flutter Plasma demo](https://flutterplasma.dev/)’s showroom page is an example of the [url_strategy plugin](https://pub.dev/packages/url_strategy), based on Flutter’s custom URL strategies, in action." /> + + +Hyperlinks are also critical to how users navigate the web. A new [`link widget`](https://pub.dev/documentation/url_launcher/latest/link/Link-class.html) in the `url_launcher` package enables users to deep link to anchors within your app or to external websites. You can use `link` on relevant widgets, including buttons, inline text, images, and specify whether the link opens in the same tab or in a new tab. + +Another integral part of any app is text rendering. Developing a layout system for text was one of the biggest challenges to support Flutter on the web. Since the web lacks a direct-text layout API, Flutter must perform various measurements on the [`paragraph`](https://api.flutter.dev/flutter/dart-ui/Paragraph-class.html) by triggering a [`layout()`](https://api.flutter.dev/flutter/dart-ui/Paragraph/layout.html). These measurements can become quite expensive, so a new [canvas-based text measurement](https://github.com/flutter/flutter/issues/33523) adds supports for both plain and rich text. Now, Flutter can do detailed measurements efficiently on the web, enabling things like painting highlight boxes accurately on selected text. + +Interacting with text is just as important as being able to render text quickly and accurately. Text can now be selected, copied, *and* pasted by using `SelectableText` and `EditableText` widgets. Also, form text fields support [autofill](https://api.flutter.dev/flutter/widgets/AutofillGroup-class.html) by enabling the browser to store data and handle cases for future autofills. + +Flutter 2 is particularly suitable for implementing Progressive Web Apps, or PWAs. PWAs are a good fit for bridging the gap between mobile and web apps in a safe and trusted manner through the efforts of Chrome’s [Project Fugu](https://web.dev/fugu-status/). + +<DashImage figure src="images/0W5BGuCaX7v9rvtjh.webp" alt="[Invoice Ninja](https://www.invoiceninja.com/), an invoice management app, launched the PWA app using the same codebase as their existing Flutter mobile apps." caption="[Invoice Ninja](https://www.invoiceninja.com/), an invoice management app, launched the PWA app using the same codebase as their existing Flutter mobile apps." /> + + +When you create a Flutter web app, we include a PWA web manifest file, as well as code to setup service workers. The [manifest file](https://developer.mozilla.org/en-US/docs/Web/Manifest) provides metadata about how your app should run, including information like icons and an app title. [Service workers](https://developers.google.com/web/ilt/pwa/introduction-to-service-worker) enable caching of resources and running your app offline. When running a Flutter app in the browser as a PWA, you’ll see the opportunity to install it to your device, whether as a mobile or desktop app. + +### Support for desktop form factors + +We want to make Flutter web experiences feel right, regardless of the shape and size of your browser window. On mobile browsers, Flutter apps already have excellent support for gestures and scrolling physics inherited from mobile app support. But desktop browsers offer different UI affordances, and so we’ve updated Flutter accordingly. + +For instance, there is an expectation for content on the desktop to display scrollbars that can be controlled by a mouse or keyboard. So the new [customizable interactive scrollbars](http://flutter.dev/go/update-scrollbars) support a [theme](https://api.flutter.dev/flutter/material/ScrollbarTheme-class.html), a track, and the ability to scroll by dragging a thumb. The [`PrimaryScrollController`](https://api.flutter.dev/flutter/widgets/PrimaryScrollController-class.html) has been expanded so that you can use[ keyboard shortcuts to scroll](http://flutter.dev/go/default-scroll-action) without having to wire up your own scroll views. + +<DashImage figure src="images/06XtFgsx4cz1OY88N.jpg" alt="Property management solution for [Zurich Insurance](https://www.zurich.com/), built by [Spica Technologies](https://spicatech.co.uk/), is a great example of the business apps Flutter’s web support can enable on desktop browsers." caption="Property management solution for [Zurich Insurance](https://www.zurich.com/), built by [Spica Technologies](https://spicatech.co.uk/), is a great example of the business apps Flutter’s web support can enable on desktop browsers." /> + + +We also increased the [default content density](https://github.com/flutter/flutter/issues/43350), because mouse pointers support a tighter density than touch devices. And we added a superset of [system mouse cursors](https://github.com/flutter/flutter/issues/60641) in the framework to support all platforms. + +Finally, to support *all* users, Flutter’s web semantic features are expanded to support accessibility for Windows, macOS, and ChromeOS. On the web, a second DOM tree called the [`SemanticsNode`](https://api.flutter.dev/flutter/semantics/SemanticsNode-class.html) tree is generated in parallel to the [`RenderObject`](https://api.flutter.dev/flutter/rendering/RenderObject-class.html) DOM tree. The `SemanticsNode` tree translates the flags, actions, labels, and other semantic properties into [ARIA attributes](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA). Now you can use [Narrator](https://support.microsoft.com/en-us/windows/complete-guide-to-narrator-e4397a0d-ef4f-b386-d8ae-c172f109bdb1), [VoiceOver](https://www.apple.com/accessibility/vision/), [TalkBack](https://support.google.com/accessibility/android/answer/6007100?hl=en), or [ChromeVox](https://support.google.com/chromebook/answer/7031755?hl=en#:~:text=You%20can%20turn%20ChromeVox%20on,then%20ChromeVox%20will%20start%20speaking.) screen readers to navigate a Flutter web app. + +### Plugin ecosystem + +Finally, web support was added to some of the most-used plugins, making it possible to bring your existing Flutter apps to the web. [Flutter plugins](https://pub.dev/) allow your code to interact with native libraries for the platform you are running on. When you run your Flutter app on the web, you can access existing JavaScript libraries through plugins. + +Since the beta release and with help from the community, support for the following plugins was added: + +* [image_picker](https://pub.dev/packages/image_picker_for_web) + +* [google_maps](https://pub.dev/packages/google_maps) + +* [firebase_analytics](https://pub.dev/packages/firebase_analytics) + +* [firebase_storage](https://pub.dev/packages/firebase_storage) + +* [connectivity](https://pub.dev/packages/experimental_connectivity_web) + +* [cloud_firestore](https://pub.dev/packages/cloud_firestore) + +* [cloud_functions](https://pub.dev/packages/cloud_functions) + +* [cross_file](https://pub.dev/packages/cross_file) + +## Looking forward + +A few years ago, it wouldn’t have been possible to deliver Flutter on the web with an acceptable level of quality and performance. However, the introduction of new web technologies and continued advances in the platform allowed us to harness much more of the potential of the underlying device. With support for the web, Flutter encompasses every device on the internet with a consistent experience across all modern browsers and devices. + +Much of this release was defined by the feedback shared by early web adopters, and by issues filed by the community. And for that, we thank you! Going forward, our primary goal is to address your feedback and resolve issues quickly so that you can stay focused on shipping a high-quality, Flutter app across *all* of your target platforms. + +<DashImage figure src="images/0eLfOUS1mLRtcDr4j.webp" alt="[Moi Mobiili](https://www.moi.fi/), a modern Mobile Virtual Network Operator, recently launched their web app using Flutter." caption="[Moi Mobiili](https://www.moi.fi/), a modern Mobile Virtual Network Operator, recently launched their web app using Flutter." /> + + +Performance will likely always be an area of investment. Our goal is to reduce code size and increase frames per second (fps). Today, every Flutter web app downloads the engine code that it needs. We’re looking into possibilities to cache some of this logic, reducing startup time and download size. We recently did work in the Flutter Gallery demo app to reduce code size using deferred libraries and plan to share what we learned soon. + +We’re also continuing to refine a few areas over the coming months: + +* While CanvasKit is stable, there are a couple of edge cases that aren’t covered, such as [font fallbacks](https://github.com/flutter/flutter/issues/74741) for special characters or properly supporting [Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) images. + +* PWAs currently [cache a subset of resources](https://github.com/flutter/flutter/issues/75861), so our full offline support still requires [extra manual steps to work for CanvasKit](https://github.com/flutter/flutter/issues/70101). + +* Text rendering and functionality, like selecting heavily styled text, is still one of the most complex features we will continue to work on. + +* Our plugin ecosystem is something we will also continue to invest in so that Google-published packages have greater parity across mobile and web. + +<DashImage figure src="images/0O9LlG-FDFRIFAsX0.webp" alt="[Simplebet](https://simplebet.io/) used Flutter’s web support to build highly interactive embeddable NFL & NBA betting experiences within Fanduel’s existing suite of mobile apps." caption="[Simplebet](https://simplebet.io/) used Flutter’s web support to build highly interactive embeddable NFL & NBA betting experiences within Fanduel’s existing suite of mobile apps." /> + + +## Getting started with Flutter on the web + +With the portability of Dart, the power of the web platform, and the flexibility of the Flutter framework, you can now build apps for iOS, Android, and the browser from the same codebase. + +For those of you who already have an existing Flutter web app, you can now build your app in the [stable channel](https://github.com/flutter/flutter/wiki/Flutter-build-release-channels#stable). If you are new to building Flutter web apps, check out [flutter.dev/web](http://flutter.dev/web), our [get started codelab](https://flutter.dev/docs/get-started/codelab-web), and the Flutter Engage [web breakout session](https://youtu.be/HAstl_NkXl0). And, as you’re building your web app, be sure to file any issues on [GitHub](https://goo.gle/flutter_web_issue). + +We can’t wait to see what you build with Flutter’s new web support! \ No newline at end of file diff --git a/sites/www/content/blog/flutter-web-support-updates/images/0K0gsPz3gj7SVvjr1.webp b/sites/www/content/blog/flutter-web-support-updates/images/0K0gsPz3gj7SVvjr1.webp new file mode 100644 index 0000000000..785af930b3 Binary files /dev/null and b/sites/www/content/blog/flutter-web-support-updates/images/0K0gsPz3gj7SVvjr1.webp differ diff --git a/sites/www/content/blog/flutter-web-support-updates/images/0Zg-bAhzitlDDVU9e.webp b/sites/www/content/blog/flutter-web-support-updates/images/0Zg-bAhzitlDDVU9e.webp new file mode 100644 index 0000000000..5e2c7e2b68 Binary files /dev/null and b/sites/www/content/blog/flutter-web-support-updates/images/0Zg-bAhzitlDDVU9e.webp differ diff --git a/sites/www/content/blog/flutter-web-support-updates/images/0hWsTPLh33YwdFhYG.webp b/sites/www/content/blog/flutter-web-support-updates/images/0hWsTPLh33YwdFhYG.webp new file mode 100644 index 0000000000..09c9b0c70e Binary files /dev/null and b/sites/www/content/blog/flutter-web-support-updates/images/0hWsTPLh33YwdFhYG.webp differ diff --git a/sites/www/content/blog/flutter-web-support-updates/images/0qu7hcAYfSXorb3jx.webp b/sites/www/content/blog/flutter-web-support-updates/images/0qu7hcAYfSXorb3jx.webp new file mode 100644 index 0000000000..c17278aa3c Binary files /dev/null and b/sites/www/content/blog/flutter-web-support-updates/images/0qu7hcAYfSXorb3jx.webp differ diff --git a/sites/www/content/blog/flutter-web-support-updates/images/19FIv19QYzuwBQ-4Hx3EkGA.webp b/sites/www/content/blog/flutter-web-support-updates/images/19FIv19QYzuwBQ-4Hx3EkGA.webp new file mode 100644 index 0000000000..5a480b4394 Binary files /dev/null and b/sites/www/content/blog/flutter-web-support-updates/images/19FIv19QYzuwBQ-4Hx3EkGA.webp differ diff --git a/sites/www/content/blog/flutter-web-support-updates/images/1w9xtjSXgRDPcCtNJLYhzpQ.webp b/sites/www/content/blog/flutter-web-support-updates/images/1w9xtjSXgRDPcCtNJLYhzpQ.webp new file mode 100644 index 0000000000..bca66dbdf2 Binary files /dev/null and b/sites/www/content/blog/flutter-web-support-updates/images/1w9xtjSXgRDPcCtNJLYhzpQ.webp differ diff --git a/sites/www/content/blog/flutter-web-support-updates/index.md b/sites/www/content/blog/flutter-web-support-updates/index.md new file mode 100644 index 0000000000..1a2e76478f --- /dev/null +++ b/sites/www/content/blog/flutter-web-support-updates/index.md @@ -0,0 +1,158 @@ +--- +title: "Flutter web support updates" +description: "PWAs, better debugging, and CanvasKit-based rendering" +publishDate: 2020-04-29 +author: mariam_hasnany +image: images/1w9xtjSXgRDPcCtNJLYhzpQ.webp +category: announcements +layout: blog +--- + +Almost a year ago, we shared our first technical preview of Flutter’s support for targeting the web. Our vision for Flutter is to provide a portable toolkit for building beautiful experiences wherever you might want to paint pixels on the screen. Given the pervasiveness of the web, it’s a natural place to experiment with Flutter, but its characteristics are different from a mobile platform like Android and iOS. + +Having Flutter code run in a web browser opens up an array of intriguing possibilities, including: + +* Providing an easy path for developers to take their existing mobile apps to the web, whether as a fully-fledged app, PWA, or as a low-friction preview of the installed experience. + +* Enabling desktop app developers to build once and make a late-bound decision about whether to ship as a high-performance installed desktop app and/or a friction-free ephemeral web app. + +* Tools like [DartPad](https://dartpad.dev/b3ccb26497ac84895540185935ed5825) and [CodePen](https://codepen.io/topic/flutter/templates) that let you quickly experiment with Dart code, seeing the results in real time without needing to install anything locally on your machine. + +* Sharing code between mobile and web applications with minimal compromise. + +Flutter was built to be unapologetically app-centric in its framework and API choices. We believe it is a distinct offering that contributes to the broad tapestry of web development. If we are successful at our goals, Flutter’s web support will provide a framework that takes full advantage of all that the web platform offers, allowing developers to build beautiful rich interactive applications that feel completely at home in a browser. + +<DashImage figure src="images/1w9xtjSXgRDPcCtNJLYhzpQ.webp" alt="[Ampstor](https://ampstor.app/): a no-code tool for building better storytelling web pages, built with Flutter’s web support." caption="[Ampstor](https://ampstor.app/): a no-code tool for building better storytelling web pages, built with Flutter’s web support." /> + + +[Ampstor](https://ampstor.app/) is a powerful example. The team set out to build an editing tool for building [AMP Stories](https://amp.dev/about/stories/), a new format that helps content providers create visual narratives for mobile devices. Here’s what they had to say about using Flutter on the web: +> “As a startup that helps customers build highly-branded experiences, it’s critical that we can get to market quickly and iterate based on customer feedback. We chose Flutter because it gave us the freedom to focus on the product, not the platform itself. We’re delighted with our experiences so far and our customers love the app we’ve built.” + +Our early goal with web support was to prove the basic hypothesis that the Flutter framework could be ported to the web. Last summer, we unforked the web code back into the main framework, leading to a single codebase that supports mobile and web (as well as desktop!). Our focus has since shifted to delivering fast, jank-free performance using the latest web platform advances, improving behavioral consistency across Flutter target platforms, and rooting out and fixing scenarios that don’t yet feel natural on the web. + +With that in mind, here is an update on what the team has been working on since web support landed in the beta channel last December. + +## What’s new? + +### PWA Support + +Fans of [Progressive Web Apps](https://web.dev/what-are-pwas/) will be delighted to know that the default Flutter template for web apps now includes support for the core features needed for an installable, offline-capable PWA app: + +<DashImage figure src="images/0qu7hcAYfSXorb3jx.webp" alt="The Flutter “counter” template app, running as a PWA on macOS." caption="The Flutter “counter” template app, running as a PWA on macOS." /> + + +Notice that this looks like a normal desktop app, but it’s really a Flutter web app that’s been installed into the browser as a PWA. You can see the difference with the added icon buttons in the title bar, which exposes Chrome functionality. Flutter-based PWAs can be [installed in the same way as any other web-based PWA](https://web.dev/install-criteria/); the settings signaling that your Flutter app is a PWA are provided by `manifest.json`, which is produced by `flutter create` in the `web/` subfolder. + +PWAs are supported on major mobile and desktop browsers and are just one of the ways we are investing in [bridging the gap](https://www.youtube.com/watch?v=JKVZMqpiY7w) between the capabilities of the native platform and the web. + +This remains a work in progress, so please give us feedback if you see something that doesn’t look right. + +### Plugins + +When we launched beta, only a few plugins supported the web. Since then, we’re seeing [more plugins](https://pub.dev/flutter/packages?platform=web) add support for the web platform, thanks to some great contributions from the community, such as: + +* [audioplayers](https://pub.dev/packages/audioplayers): play multiple audio files simultaneously + +* [connectivity](https://pub.dev/packages/experimental_connectivity_web): allows apps to discover network connectivity and configure accordingly + +* [cloud_firestore](https://pub.dev/packages/cloud_firestore): NoSQL cloud database, accessed through the Cloud Firestore API + +* [cloud_functions](https://pub.dev/packages/cloud_functions): access serverless cloud functions, hosted by Firebase + +While we plan on extending a few more popular Google-authored plugins for the web ourselves, we largely depend on the community to help bring web support to existing packages. To help you get started, we have published a couple of articles explaining [how to write a web plugin](https://medium.com/flutter/how-to-write-a-flutter-web-plugin-5e26c689ea1). + +### **Web Debugging with Expression Evaluation** + +Debugging is a crucial step in the development process, and is one of the areas of active development for creating a richer Flutter web experience. If you use VS Code to develop Flutter applications, you’re probably expecting the full set of debug capabilities to be available to you, including evaluating expressions in the debug window, using the watch functionality, or hovering over variables to see their value. + +Until recently, this was only available for debugging mobile Flutter apps. In the last couple of months, we have been working on connecting expression evaluation to Flutter on the web and now have experimental support ready to try out in VSCode. Support for other IDEs, such as [IntelliJ](https://github.com/flutter/flutter-intellij/issues/4519), is still in progress. + +This feature is available on the Flutter dev or master channel. Use the following instructions to set it up for VS Code: + +Configure your project to [run on the web](https://flutter.dev/docs/get-started/web). Within VS Code, enable expression evaluation for web in `launch.json`: + +<DashImage figure src="images/0K0gsPz3gj7SVvjr1.webp" /> + + +Set a breakpoint and start debugging. Try expression evaluation in the debug console using the expression evaluation box as shown below. Additionally, you can use the watch window or hover over variables to evaluate your code using expressions. + +<DashImage figure src="images/0Zg-bAhzitlDDVU9e.webp" /> + + +You can also try setting conditional breakpoints using expressions: + +<DashImage figure src="images/0hWsTPLh33YwdFhYG.webp" /> + + +### Testing + +The latest builds of Flutter now support running automated Flutter driver UI tests against your Flutter web apps in just the same way that they work for mobile apps. To enable support for this experimental feature, you can [read the directions on the Flutter wiki](https://github.com/flutter/flutter/wiki/Running-Flutter-Driver-tests-with-Web). + +## Current priorities: testing, quality and performance + +Over the past few months, our focus has been spent on improving the infrastructure, quality and rendering for Flutter’s web support. While we still have a lot to do, we have made significant progress in all three areas. + +### **Test infrastructure and benchmarks** + +Building test infrastructure is one of those improvements that isn’t always visible to those developing with Flutter but is nonetheless important to building a stable version of Flutter on the web. + +This past quarter we have added automated testing to ensure correctness of the web engine and framework. We added screenshot tests on Chrome to make sure we are rendering consistently and correctly as we make changes to the code. We now run unit and integration testing on Chrome, Firefox and Safari. Running these tests on mobile browsers and Internet Explorer/Edge will be coming online soon. Additionally, we have added performance benchmark tests, enabling us to iterate quickly without introducing regressions. + +### Quality + +Speaking of issues, we’ve been busy burning down issues to incrementally improve our quality through optimizations and correctness fixes. + +Recently we landed an optimization in Flutter for web that improves scrolling for static content, meaning content that is not lazily loaded but is rendered all in the same frame. This should bring scrolling performance more in line with traditional web experiences. + +We’ve also fixed several text rendering issues such as text disappearing behind the canvas or cursor problems with multiline text. These text correctness fixes are currently enabled behind the `FLUTTER_WEB_USE_EXPERIMENTAL_CANVAS_TEXT` flag; our plan is to make this the platform default soon. + +At [Flutter Interact](http://g.co/FlutterInteract) last year, we announced that our new Gallery sample app runs on the web using Flutter. Since then we’ve made significant performance optimizations in web support as well as the app, so that it runs even faster and smoother. As a result of these changes, initial load time is now three times faster, and the code size has been reduced by 2.7x. + +<DashImage figure src="images/19FIv19QYzuwBQ-4Hx3EkGA.webp" alt="The Flutter Gallery app, running as a PWA on Windows." caption="The Flutter Gallery app, running as a PWA on Windows." /> + + +Stay tuned for an upcoming blog series that will share more about what we’ve learned, as well as best practices for optimizing your Flutter app for performance. Until then, check out the updated [Flutter Gallery app](https://gallery.flutter.dev) for yourself. + +### Rendering improvements with CanvasKit + +When we started our exploration into the web, we considered a variety of approaches for rendering UI, knowing that our choice would dictate how well we could build, layout, and paint Flutter widgets on the web. + +We originally opted for an HTML DOM-based model that combines HTML, CSS, and the [Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) to express output produced by the Flutter framework. We call this implementation our DomCanvas rendering system. + +Over the last year, we have gained a better understanding of the performance and correctness characteristics of the various rendering technologies available on the web, and have been experimenting with a second approach that uses [CanvasKit](https://skia.org/user/modules/canvaskit). CanvasKit brings [Skia](https://skia.org/) to the web using [WebAssembly](https://webassembly.org/) and [WebGL](https://get.webgl.org/), enabling a hardware-accelerated drawing surface that improves our ability to render complex and intensive graphics efficiently. + +We believe that both DomCanvas and CanvasKit offer advantages in different scenarios, and so we’re supporting both rendering models. + +* DomCanvas offers the greatest compatibility with a wide variety of browsers, with a compact code size. However, repaint performance is poorer, which makes it less suitable for more graphically-intensive applications. + +* The CanvasKit backend offers superior performance, fidelity, and correctness, but it has poor initial startup time due to a larger code size. + +By default, Flutter’s web support uses DomCanvas, but you can enable the CanvasKit rendering engine with this command: + +``` +flutter run -d chrome --release + --dart-define=FLUTTER_WEB_USE_SKIA=true +``` + + +One word of caution: the CanvasKit engine still has a few rough edges; for early adopters who are putting Flutter into production, the DomCanvas engine offers the greatest stability. + +## Known issues on our radar + +While we believe we’re making good progress at building a solid foundation, there are a few other areas we are planning to work on to fill out our web support. + +* **Desktop-class UX.** Supporting Flutter on desktop web browsers requires more than web support: there are typically significant UI differences between mobile and desktop experiences. We’re therefore adding features to support desktop-class experiences, such as [responsive widgets](https://material.io/components/navigation-rail/) and scrolling physics, that still need to be implemented. + +* **Autofill** is a feature supported in most platforms, and in terms of the web, it means enabling the browser to store data and fill it in where appropriate. We [recently added initial autofill support](https://github.com/flutter/flutter/pull/52126) to the core framework, and now we are working on [adding this feature to Flutter on the web](https://github.com/flutter/engine/pull/17986). This effort includes addressing browser differences, [saving fill information for future autofills,](https://github.com/flutter/flutter/issues/55613) and passing auto-fillable elements as a group to the engine. + +* **URL Routing.** It’s a small issue, perhaps, but today Flutter web apps include a [hash marker for routing](https://github.com/flutter/flutter/issues/33245) even on the home page. We’re working to eradicate this and provide more control over URL routing, with [contributions from the community](https://github.com/flutter/engine/pull/17829) under review at present. + +* **Code size** remains an area of active investigation. Today every Flutter web app downloads the engine code that it needs; we’re looking into possibilities to cache some of this logic to reduce startup time and download size. + +## Call to action + +We hope there are some fun surprises in this article: we’ve made a lot of progress since the December update. However, you should be aware that Flutter’s web support is still in beta, and you should be cautious about deploying it into production environments, since performance and browser compatibility work is still in progress. + +If you are new to developing with Flutter on the web, check out [flutter.dev/web](https://flutter.dev/web) to learn more and try out your code on [dartpad.dev](https://dartpad.dev/) or [codepen.io/flutter](https://codepen.io/flutter). + +For those that are currently developing Flutter web apps, thank you for your support and contributions, we hope you continue experimenting, [filing issues](https://github.com/flutter/flutter/issues/new?title=[web]:+%3Cdescribe+issue+here%3E&labels=%E2%98%B8+platform-web&body=Describe+your+issue+and+include+the+command+you%27re+running,+flutter_web%20version,+browser+version) and [updating plugins](https://medium.com/flutter/how-to-write-a-flutter-web-plugin-part-2-afdddb69ece6) to support the web! \ No newline at end of file diff --git a/sites/www/content/blog/flutter-whats-next-on-the-web/images/17iBUzC4UvwNi8agIsCrZ7w.webp b/sites/www/content/blog/flutter-whats-next-on-the-web/images/17iBUzC4UvwNi8agIsCrZ7w.webp new file mode 100644 index 0000000000..c62db2adb2 Binary files /dev/null and b/sites/www/content/blog/flutter-whats-next-on-the-web/images/17iBUzC4UvwNi8agIsCrZ7w.webp differ diff --git a/sites/www/content/blog/flutter-whats-next-on-the-web/index.md b/sites/www/content/blog/flutter-whats-next-on-the-web/index.md new file mode 100644 index 0000000000..76639edfc5 --- /dev/null +++ b/sites/www/content/blog/flutter-whats-next-on-the-web/index.md @@ -0,0 +1,140 @@ +--- +title: "Flutter: What’s next on the Web?" +description: "Our priorities for upcoming releases, focusing on performance, developer experience and web integration" +publishDate: 2021-09-14 +author: mariam_hasnany +image: images/17iBUzC4UvwNi8agIsCrZ7w.webp +category: announcements +layout: blog +--- + +<DashImage figure src="images/17iBUzC4UvwNi8agIsCrZ7w.webp" /> + + +Flutter’s web support [hit the stable milestone](https://medium.com/flutter/flutter-web-support-hits-the-stable-milestone-d6b84e83b425) in March 2021. So what’s next? + +According to our user research, the web is the target for over 10% of Flutter projects. So our focus is now on improving the quality of the initial stable release and enabling more of you to ship your Flutter web apps to production. + +We established these priorities based on the findings from Flutter’s quarterly surveys and what we heard from you in issues, face-to-face discussions, and on social media. We just received the results for the Q3 survey and are pleased to see that this plan is in line with your feedback. + +This article provides more detail into our roadmap and the work planned for each priority. Some features may span several quarters, and others require investigation (🔍) before we can commit to a solution. + +## WEB LOOK AND FEEL + +It’s important that Flutter web apps feel natural on the web. This includes features such as scrolling behavior, text functionality, splash screens, hyperlinks, SEO, and other web-specific UI features. + +### **RTL text** + +Previously, Flutter offered incomplete web support for right-to-left (RTL) languages such as Arabic and Hebrew. While the framework itself supported RTL text, the web engine produced undefined behavior because it ignored the distinction between LTR and RTL. + +The recent stable release (2.5) added basic support for RTL so that all major use cases for RTL languages are supported and usable on Flutter web apps. Most of the issues related to basic support have been addressed and we plan to fix the remaining issue. + +* [Implement RTL support for text editing](https://github.com/flutter/flutter/issues/32239) (fixed) + +* [RTL text rendering glitch on web](https://github.com/flutter/flutter/issues/69396) (fixed) + +* [TextField with `TextDirection.rtl` has misplaced cursor on web](https://github.com/flutter/flutter/issues/78550) (planned) + +* [InlineSpans in RichText overlaps in RTL application](https://github.com/flutter/flutter/issues/82136) (fixed) + +### **Scrolling Behavior** + +While Flutter 2 included [several improvements](https://github.com/flutter/flutter/pulls?q=is%3Apr+is%3Aclosed+is%3Amerged+label%3A%22f%3A+scrolling%22) to support scrolling behavior on desktop browsers (such as scroll bar support), scrolling behavior is still an area that at times doesn’t look or feel right on the browser or on the OS that the web app is running on. + +While some of these behaviors depend on the work for Flutter desktop support, there are some issues (listed below) that we plan to address in this roadmap specific to scrolling physics and scrollbars. We also plan to investigate trackpad support. + +* [PageScrollPhysics has weird behavior](https://github.com/flutter/flutter/issues/35687) + +* [No scrollbar in (infinite) list widget](https://github.com/flutter/flutter/issues/41434) + +* [Mac desktop scroll physics should be range maintaining](https://github.com/flutter/flutter/issues/85579) + +* [Let scrollbars avoid obstructing slivers and media query paddings](https://github.com/flutter/flutter/issues/13253) + +* [MaterialScrollBehavior.buildScrollbar needs update](https://github.com/flutter/flutter/issues/87739) + +🔍 Research for [trackpad support](https://github.com/flutter/flutter/issues/23604) + +### **App loading API** + +Some web apps prefer to show a splash screen, loading indicator, or landing page when an app is loading or to offer a custom experience. Currently, there is no easy way to implement a custom experience or indicate that a Flutter web app is loading; this became more critical when rendering with the CanvasKit’s larger bundle size. + +Our work here focuses on providing you with an explicit API for the application start up cycle that can be used to preload the app, control the app’s loading cycle, and build splash screens or loading indicators. + +* [Add support for splash screen on the web](https://github.com/flutter/flutter/issues/48468) + +## ACCESSIBILITY + +Accessibility is one of our top priorities; we aim to provide you with the tools necessary to build accessible web apps that work well on the most common screen readers. + +Flutter 2.2 made significant improvements to accessibility support. Since then, we’ve heard concerns from end users trying to traverse their web apps with screen readers, such as [JAWS](https://www.freedomscientific.com/products/software/jaws/). + +In this roadmap, we are focusing on the issues with [supported screen readers ](https://flutter.dev/docs/development/accessibility-and-localization/accessibility#screen-readers)for desktop browsers, such as snackbar announcements. We will also continue to investigate ways to improve our overall accessibility support. + +* [Fail to announce button press when using enter key](https://github.com/flutter/flutter/issues/83812) + +* [Arrow and B key do not update the screen reader focus](https://github.com/flutter/flutter/issues/83809) + +## PERFORMANCE + +Performance is an area we will always prioritize to improve the user experience with Flutter web apps. Currently, our main focus will improve scrolling jank, and speed up the initial load of web apps. + +### **Scrolling jank** + +Based on our recent quarterly survey, scrolling jank was one of the top performance issues reported. Our goal is to ensure that Flutter web apps scroll smoothly using gestures on mobile and mouse/keyboard on desktop, but that also depends on the type and quantity of content a web app expects a user to scroll through. + +In the coming months we will primarily focus on improving jank due to image decoding, and we will continue investigating scrolling performance issues to find other use cases we can improve. + +* [Move image decoding to web workers](https://github.com/flutter/flutter/issues/63397) + +* [Make platform views less expensive in the CanvasKit renderer](https://github.com/flutter/flutter/issues/71884) + +### **Bundling CanvasKit (offline support)** + +Currently, Flutter web apps rendered in CanvasKit require extra manual steps to work offline as a Progressive Web App (PWA). In order to work fully as a PWA in offline mode out of the box, and ensure that the app meets strict content security policies, we need to bundle CanvasKit and font fallbacks. + +We will start by bundling CanvasKit first, then bundle fonts, and add the tooling necessary to enable offline mode. + +* [CanvasKit backend should not depend on volatile Google Fonts](https://github.com/flutter/flutter/issues/85793) + +* [Support bundling CanvasKit instead of CDN](https://github.com/flutter/flutter/issues/70101) + +### **CanvasKit download size** + +CanvasKit offers better performance than DOM-based approaches, and is therefore our default renderer on desktop browsers. However, the time needed to download an app impacts initial load performance (and the Lighthouse score for Flutter apps running on the web). + +In this roadmap, we will investigate ways to reduce the download size of CanvasKit in an effort to improve initial load performance. We want to ensure that the end user’s devices or browser isn’t dealing with a massive payload. + +🔍 [Improve CanvasKit download size](https://github.com/flutter/flutter/issues/89616) + +🔍 [Find a reasonable sized emoji font fallback](https://github.com/flutter/flutter/issues/76248) + +## INTEGRATION WITH HTML CONTENT + +The ability to integrate with browser code is important for leveraging the strength of the web platform. There are two ways for Flutter web apps to integrate with HTML: 1) using HTML platform views within Flutter web apps, or 2) embedding Flutter as a content island into an existing web app (sort of an add to app for web). While the former exists today and may need improvement, the latter will be a new feature that needs further design and development. + +### **Embedding with custom element (Add to app)** + +Today, the only way to embed a Flutter web app into an existing website/web app is through an iframe. While that works for some use cases, it is not always an ideal solution for those slowly migrating their web app to use Flutter. + +In this roadmap, we will investigate and design a custom solution that enables you to embed your Flutter web apps, similar to the add-to-app mobile scenario. + +🔍 [Render app inside custom element](https://github.com/flutter/flutter/issues/32329) + +## DEVELOPER EXPERIENCE + +Flutter’s ecosystem includes features that you use for development but currently lack web support, such as plugins, debugging, hot reload, and so on. In order for you to have a great developer experience on the web, we will continue to bridge the parity gap between mobile and web. + +### **Camera plugin** + +A camera plugin has been one of the most requested plugins since our initial stable release; many of you find the parity gap to be a blocker when bringing Flutter mobile apps to the web. + +In the Flutter 2.5 stable release, we delivered an early version of a [plugin](https://pub.dev/packages/camera_web) that initializes the camera, shows a camera preview, and takes a picture. We will make improvements to this plugin as we receive feedback. + +* [Add camera support for web](https://github.com/flutter/flutter/issues/45297) (fixed with some PRs pending) + +## Thank you! + +While this is an overview of our current roadmap for the web, our [GitHub issue list](https://github.com/flutter/flutter/issues?q=is%3Aopen+is%3Aissue+label%3Aplatform-web+) remains the source of truth for what we’re working on. We may add, prolong, or defer features based on what we learn and what you tell us. + +As always, we greatly value your feedback and appreciate your support! \ No newline at end of file diff --git a/sites/www/content/blog/flutters-ios-application-bundle/images/1xfvovQJT-5OsHBDBIQa92A.webp b/sites/www/content/blog/flutters-ios-application-bundle/images/1xfvovQJT-5OsHBDBIQa92A.webp new file mode 100644 index 0000000000..c56195d7b6 Binary files /dev/null and b/sites/www/content/blog/flutters-ios-application-bundle/images/1xfvovQJT-5OsHBDBIQa92A.webp differ diff --git a/sites/www/content/blog/flutters-ios-application-bundle/index.md b/sites/www/content/blog/flutters-ios-application-bundle/index.md new file mode 100644 index 0000000000..c43224d71a --- /dev/null +++ b/sites/www/content/blog/flutters-ios-application-bundle/index.md @@ -0,0 +1,174 @@ +--- +title: "Flutter’s iOS Application Bundle" +description: "This document describes how Flutter’s build system converts your Flutter project (along with its assets), into an iOS application bundle. I…" +publishDate: 2018-07-20 +author: chinmaygarde +image: images/1xfvovQJT-5OsHBDBIQa92A.webp +category: tutorial +layout: blog +--- + +This document describes how Flutter’s build system converts your Flutter project (along with its assets), into an iOS application bundle. I hope to demystify some of the build steps and explain the purpose of generated artifacts, so that you can integrate the same processes into your custom build environment. + +**A note about workflow**: When building your application for release, you may use Flutter tooling, which makes the build process easy. However, some developers may find that this process is not as configurable, or doesn’t fit well in their custom build or continuous integration (CI) setups. + +If you have a custom Xcode build or Gradle setup, all the "magic" added by Flutter tooling is optional, and you can configure the build to suit your own workflow. + +All the information in this document applies to iOS application bundles prepared for release to the App Store. This means projects built to Flutter’s [release mode](https://github.com/flutter/flutter/wiki/Flutter's-modes). Compiling to either debug or profile mode uses a different runtime and packaging model in order to support tooling such as [hot reload](https://flutter.io/hot-reload/) and [observatory](https://dart-lang.github.io/observatory/get-started.html). + +A Flutter application renders the user interface into a single view in the native view hierarchy. + +<DashImage figure src="images/1xfvovQJT-5OsHBDBIQa92A.webp" alt="The Flutter view hierarchy visualized in Xcode. There is not much to see." caption="The Flutter view hierarchy visualized in Xcode. There is not much to see." /> + + +## The Application Bundle + +The application bundle created using the `flutter build ios --release` command (or using your IDE) looks remarkably similar to a typical iOS app bundle that contains the application executable along with any referenced frameworks and resources. + +Flutter’s generated bundle for the `Runner` app: + +``` +Runner.app +├── AppFrameworkInfo.plist +├── Assets.car +├── Base.lproj +│ ├── LaunchScreen.storyboardc +│ └── Main.storyboardc +├── Debug.xcconfig +├── Frameworks +│ ├── App.framework # See “App framework bundle” +│ │ ├── App +│ │ ├── Info.plist +│ └── Flutter.framework # See “Flutter framework bundle” +│ ├── Flutter +│ ├── Info.plist +│ └── icudtl.dat +├── Info.plist +├── Runner +└── flutter_assets +├── AssetManifest.json +├── FontManifest.json +├── LICENSE +├── fonts +└── packages +└── cupertino_icons +└── assets +└── CupertinoIcons.ttf +``` + + +## Compiling the Application + +When compiling a release mode version of the app (as opposed to [profile or debug modes](https://github.com/flutter/flutter/wiki/Flutter's-modes)), artifacts from both the [buildbots](https://build.chromium.org/p/client.flutter/waterfall) and your host machine are needed. (For more information on the buildbots, part of the engine build that uses `GN` and `Ninja`, see [Contributing to the Flutter engine](https://github.com/flutter/engine/blob/master/CONTRIBUTING.md).) + +When you install the SDK, Flutter tools are cached on your machine. You can see them by looking inside the `bin/cache` directory in your copy of the Flutter SDK. If you decide to integrate any steps of this process into your own build system, this directory contains all the versioned tools you need for working with Flutter. + +The following sections describe some of the files unique to Flutter’s iOS app bundles. + +## Flutter Engine framework bundle + +The `Flutter.framework` directory, packaged as an [iOS framework bundle](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Frameworks.html#//apple_ref/doc/uid/10000183i), includes: + +* [Flutter engine +](https://github.com/flutter/engine)Contains the core libraries (for example, graphics, file and network I/O, accessibility support, plugin architecture), the DartVM, and the Skia renderer. + +* Assets referenced by the Flutter engine +This currently is just ICU data. + +The buildbots generate the Flutter engine framework bundle, and the Flutter tools download and cache it on your machine. + +## AOT framework bundle + +The `App.framework` bundle contains an AOT snapshot of all the Dart application code written by the user, as well as the Dart code for the Flutter framework and plugins, in `armv7s` and `aarch64` formats. + +During a release build, the compiler performs tree-shaking on the Dart code, so only the code that is actually used ends up in the bundle. The `gen_snapshot` tool cached on your machine generates the artifacts necessary to create the `App.framework` bundle. + +### AOT Snapshot + +The AOT snapshot library contains the ahead-of-time code compiled from Dart to machine native bits. + +The snapshot library, generated by `gen_snapshot`, [contains four main symbols](https://github.com/flutter/engine/wiki/Flutter-Engine-Operation-in-AOT-Mode). These symbols can be dumped by the `nm` command. For example: + +``` +$ nm -gU Runner.app/Frameworks/App.framework/App +Runner.app/Frameworks/App.framework/App (for architecture armv7): +003c6f60 S _kDartIsolateSnapshotData +00007000 T _kDartIsolateSnapshotInstructions +003c16a0 S _kDartVmSnapshotData +00004000 T _kDartVmSnapshotInstructions + +Runner.app/Frameworks/App.framework/App (for architecture arm64): +00000000004041a0 S _kDartIsolateSnapshotData +0000000000009000 T _kDartIsolateSnapshotInstructions +00000000003fc740 S _kDartVmSnapshotData +0000000000005000 T _kDartVmSnapshotInstructions +``` + + +Their purpose is as follows: + +* Dart VM Snapshot (***`kDartVmSnapshotData`***): +Represents the initial state of the Dart heap shared between isolates. This helps launch Dart isolates faster, but doesn’t contain any isolate-specific information. + +* Dart VM Instructions (***`kDartVmSnapshotInstructions`***): +Contains AOT instructions for common routines shared between all Dart isolates in the VM. This snapshot is typically extremely small and mostly contains stubs. + +* Isolate Snapshot (***`kDartIsolateSnapshotData`***): +Represents the initial state of the Dart heap and includes isolate-specific information. + +* Isolate Instructions (***`kDartIsolateSnapshotInstructions`***): +Contains the AOT code that is executed by the Dart isolate. + +Invoking `gen_snapshot` is simple. You point it to the Dart source code, and it spits out a blob for each of these four symbols. Then, Xcode packages the symbols into an iOS framework bundle just like it would for a framework written in C, C++, Objective-C or Swift. + +Learn more about how to configure the snapshot and the engine [in the Flutter engine wiki](https://github.com/flutter/engine/wiki/Flutter-Engine-Operation-in-AOT-Mode#snapshot-generation). + +Apart from munging code, the Flutter tools also make sure that the assets referenced by the application (and its plugins), end up in the app bundle. It does this by reading the assets listed in the project’s `pubspec.yaml` file. + +## A Note about Android + +The process for building an Android APK bundle (using `flutter build apk — release`, or your IDE) results in the following file structure: + +``` +$ Runner.apk.unzipped +├── AndroidManifest.xml +├── assets +│ ├── flutter_assets +│ │ ├── fonts +│ │ │ └── MaterialIcons-Regular.ttf +│ │ └── packages +│ │ └── cupertino_icons +│ │ └── assets +│ │ └── CupertinoIcons.ttf +│ ├── icudtl.dat +│ ├── isolate_snapshot_data +│ ├── isolate_snapshot_instr +│ ├── vm_snapshot_data +│ └── vm_snapshot_instr +├── classes.dex +├── lib +│ └── armeabi-v7a +│ └── libflutter.so +├── output.json +├── res +└── resources.arsc +``` + + +It is mostly the same as an iOS release bundle, except: + +* The Flutter engine is packaged as an ELF library (`libflutter.so`). + +* The 4 symbols detailed in the previous section are now just binary blobs in the assets directory. + +The second point may be a bit unexpected and begs explanation: You didn’t have to download the NDK to build the release APK. That’s because, in the absence of the NDK on your machine, the Flutter tools add the blobs as assets. On Android, the Flutter engine can mark pages as executable. So, when it detects that AOT assets are packaged as binary blobs, it maps these blobs in memory, and marks the corresponding pages as executable. If you have access to the NDK on your machine, you can specify the [location to it,](https://github.com/flutter/engine/wiki/Flutter-Engine-Operation-in-AOT-Mode#android-configuration) and generate a dynamic library with these symbols. In this case, the Flutter engine uses the 4 symbols in the dynamic library instead. + +## Summary + +The key points about building an iOS app bundle are: + +* You can place a Flutter View anywhere in the native view hierarchy. All contents rendered by Flutter will be composited into this view. + +* Dart code is compiled to native machine code and packaged as a library or framework bundle just like any other C++/Objective/Swift library. This means that all the crash reporting and symbolication tools work the same way for Dart AOT code. + +* You can integrate Flutter into your own custom build system and not depend on Flutter tooling on your development machine (though using it makes your life easier). All the tools are available in the `bin/cache` directory of the Flutter SDK. \ No newline at end of file diff --git a/sites/www/content/blog/flutters-material-and-cupertino-code-freeze/images/1JN1_t6kPRkgGFVJyetZE0w.webp b/sites/www/content/blog/flutters-material-and-cupertino-code-freeze/images/1JN1_t6kPRkgGFVJyetZE0w.webp new file mode 100644 index 0000000000..75128f0c49 Binary files /dev/null and b/sites/www/content/blog/flutters-material-and-cupertino-code-freeze/images/1JN1_t6kPRkgGFVJyetZE0w.webp differ diff --git a/sites/www/content/blog/flutters-material-and-cupertino-code-freeze/images/1NkrFO7smrNSo2NofNp0EVA.webp b/sites/www/content/blog/flutters-material-and-cupertino-code-freeze/images/1NkrFO7smrNSo2NofNp0EVA.webp new file mode 100644 index 0000000000..f6861a704d Binary files /dev/null and b/sites/www/content/blog/flutters-material-and-cupertino-code-freeze/images/1NkrFO7smrNSo2NofNp0EVA.webp differ diff --git a/sites/www/content/blog/flutters-material-and-cupertino-code-freeze/index.md b/sites/www/content/blog/flutters-material-and-cupertino-code-freeze/index.md new file mode 100644 index 0000000000..ec280ac7b8 --- /dev/null +++ b/sites/www/content/blog/flutters-material-and-cupertino-code-freeze/index.md @@ -0,0 +1,50 @@ +--- +title: "Flutter’s Material and Cupertino code freeze" +description: "We’ve been hard at work preparing to decouple Material and Cupertino from the Framework, and now our first major milestone has arrived! As…" +publishDate: 2026-04-07 +author: justinmc +image: images/1JN1_t6kPRkgGFVJyetZE0w.webp +layout: blog +--- + +<DashImage figure src="images/1JN1_t6kPRkgGFVJyetZE0w.webp" alt="Material and Cupertino libraries are frozen and will be moved from the Flutter framework to new packages" caption="Material and Cupertino libraries are frozen and will be moved from the Flutter framework to new packages" /> + + +## Flutter’s Material and Cupertino code freeze + +We’ve been hard at work preparing to decouple Material and Cupertino from the Framework, and now our first major milestone has arrived! As of April 7th, all contributions to the Material and Cupertino libraries in flutter/flutter are frozen. Our next milestone will be the re-release of these libraries as the [`material_ui`](https://pub.dev/packages/material_ui) and [`cupertino_ui`](https://pub.dev/packages/cupertino_ui) packages on pub.dev. + +This means that, after the code freeze, no more changes will be allowed to the Material and Cupertino libraries inside of flutter/flutter. Further development on these libraries will resume in the [flutter/packages](https://github.com/flutter/packages) repository once the new packages are released. + +**If you write Flutter apps or plugins, but don’t contribute to Material or Cupertino itself, you can stop reading now. This won’t affect you… yet.** + +After the 3.44 stable release, the new packages will be published and developers will eventually need to migrate. The old Material and Cupertino code will be deprecated in the stable release *after* 3.44 and deleted some time after that. Of course, when the time comes, we’ll follow up with detailed instructions about this migration. + +For those who actively contribute to these libraries or are otherwise invested in their development, here are some things you should know: + +## What if you have PRs in flight? + +Despite the code freeze, we want development on Material and Cupertino to continue with minimal interruption! Any open PRs that touch Material or Cupertino should remain open, and reviewers will continue reviewing and giving feedback as usual. Once the new packages are published, we will provide instructions on how to port these kinds of PRs to flutter/packages. Eventually, your change will go out as a part of a new `material_ui` or `cupertino_ui` release. + +## How about new and existing issues related to Material and Cupertino? + +Issues that relate to Material or Cupertino will remain in flutter/flutter as always. This unified issue tracker approach is the same pattern that we follow for other packages in the flutter/packages repo and a few other repositories. + +## Why freeze the code now? + +The moment that we release the 1.0.0 versions of the `material_ui` and `cupertino_ui` packages, we think it’s important to have a seamless migration process for every Flutter developer who is ready to migrate, regardless of which release channel they’re coming from. This means that we need to keep the risk of breaking changes to an absolute minimum between the Material and Cupertino libraries in flutter/flutter and in flutter/packages. We can achieve this by freezing the code one stable release cycle ahead and copying that frozen code to the new packages. + +The first step in the migration process for Flutter developers is to perform a normal SDK migration to v3.44 or above on any channel. Once there, we know that they have a copy of Material and Cupertino that is frozen. Even if they upgrade their SDK again, that Material and Cupertino code will not change (until it’s deprecated and deleted in the long term). What’s more, we know that the frozen Material and Cupertino code is identical to the code in the 1.0.0 `material_ui` and `cupertino_ui` packages, or as close to identical as possible. From there, the developer can migrate from the Material and Cupertino code inside their copy of the SDK onto the `material_ui` and `cupertino_ui` packages with minimal friction. + +<DashImage figure src="images/1NkrFO7smrNSo2NofNp0EVA.webp" /> + + +## How we got here + +It’s been a long journey to this point with *many* contributions and feedback from across the community. A few months ago when I realized that we had test dependencies that would get in the way of decoupling, I posted an [issue](https://github.com/flutter/flutter/issues/177028) and figured I was in for a lot of migration work. Instead, contributors from across the community immediately jumped in to help migrate hundreds of tests. The support we received from first-time contributors to veterans was critical to getting us ready for decoupling. THANK YOU! + +## What’s next? + +After the code freeze, we’ll begin preparing for migration to the new `material_ui` and `cupertino_ui` packages. This includes tasks like porting the code over, implementing CI/CD, testing, and setting up docs infrastructure to make sure we can keep the same high quality developer experience that you expect from Flutter. + +As the new packages near readiness, we’ll publish more information about how to migrate successfully, so keep an eye out. Also, if you see anything that you think we’ve missed, please jump in with an issue or a PR. We couldn’t have gotten this far without help from the amazing Flutter community, and we can’t wait to see where we’ll go from here. \ No newline at end of file diff --git a/sites/www/content/blog/flutters-multiplatform-value-for-agentic-development/images/1aotSzcGPZg3gYqhwDy4LWQ.webp b/sites/www/content/blog/flutters-multiplatform-value-for-agentic-development/images/1aotSzcGPZg3gYqhwDy4LWQ.webp new file mode 100644 index 0000000000..01677413b2 Binary files /dev/null and b/sites/www/content/blog/flutters-multiplatform-value-for-agentic-development/images/1aotSzcGPZg3gYqhwDy4LWQ.webp differ diff --git a/sites/www/content/blog/flutters-multiplatform-value-for-agentic-development/index.md b/sites/www/content/blog/flutters-multiplatform-value-for-agentic-development/index.md new file mode 100644 index 0000000000..ac1a884406 --- /dev/null +++ b/sites/www/content/blog/flutters-multiplatform-value-for-agentic-development/index.md @@ -0,0 +1,47 @@ +--- +title: "Flutter’s multiplatform value for agentic development" +description: "The fundamental value of multiplatform development with Flutter lies in building apps that support multiple platforms with just a single…" +publishDate: 2026-05-18 +author: mit-mit +image: images/1aotSzcGPZg3gYqhwDy4LWQ.webp +layout: blog +--- + +The fundamental value of multiplatform development with Flutter lies in building apps that support multiple platforms with just a single, shared source codebase, allowing developer teams to work in unison across all platforms. + +This is crucial in an AI-driven world where enhanced consistency, reduced token usage, and fast market reach become vital. By maintaining a single codebase, builders can focus their AI assistants on one unified context, drastically reduce token overhead, and minimize AI hallucinations. Instead of asking AI to translate features across fragmented, platform-specific languages, builders can leverage AI to write it once in Dart and instantly deploy it everywhere. + +<DashImage figure src="images/1aotSzcGPZg3gYqhwDy4LWQ.webp" alt="Dash secretly hanging out in an alley doing agentive things" caption="Dash secretly hanging out in an alley doing agentive things" /> + + +## The existing value proposition + +Multiplatform development relies on enabling a single, shared source codebase. In our first-party Flutter apps, between 95% and 99% of the source code is shared. This massive code reuse unlocks several benefits: + +* Faster time to market across multiple platforms because a team only needs to maintain one codebase. + +* Guaranteed consistency across platforms, giving companies a single, consistent feature set to support across all their customers, regardless of their platform of choice. + +* Native performance and stability because Flutter code is compiled to each platform’s native machine code. + +* Semantic guardrails increase security because the Dart language is strongly typed. + +## The agentic value proposition + +While LLMs are good at translating requirements into code, using them to build separate native apps for each platform scales poorly. Replicating features across different languages using LLMs multiplies generation time and token usage, and can quickly lead to implementations drifting apart. + +Flutter’s single-source solution eliminates these problems. But beyond just code sharing, Flutter’s specific architecture makes it the ideal framework for agent-driven development. This emerging value proposition is driven by several key advantages: + +* **Token reduction:** By generating your app once in Dart, you drastically reduce token overhead compared to using AI to translate features across platform-specific languages. This eliminates the need to replicate logic across different codebases, which scales poorly and multiplies token usage. + +* **Consistency:** Flutter provides a unified experience because a single source codebase ensures the feature set remains identical across all platforms. This prevents platform drift that occurs when LLMs hallucinate and implementations drift apart. + +* **Self-correcting agents**: Flutter has strong semantic guardrails due to Dart’s strongly typed language, and rich developer tooling. When an AI agent generates code, the strict type system, exposed through flexible tools and MCP servers, acts as an immediate feedback loop to catch errors instantly. + +* **Predictable code generation**: LLMs excel at generating hierarchical, structured data. Flutter’s compositional, declarative UI aligns with this strength. It is much easier for an agent to reason about and reliably generate a single Dart widget tree than to manage the fragmented logic of other platform-specific frameworks. + +* **High-speed validation with Hot Reload**: In an agentic workflow, the bottleneck is often verifying the AI’s output. Flutter’s hot reload feature provides a workflow where any change made by the agent is viewable instantly in the running app during development. + +## The Flutter advantage + +Flutter’s support for a single shared codebase targeting multiple platforms, paired with a strongly typed language and powerful tooling, makes it a great companion to agent-driven development. In summary, the future looks bright! With Flutter, expect your agentically developed apps to result in low token usage, faster multi-platform development cycles, strong semantic guardrails, app consistency across platforms, and native performance. Happy building! \ No newline at end of file diff --git a/sites/www/content/blog/flutters-path-towards-seamless-interop/images/0-1m66Jxnec3s8Qnb.webp b/sites/www/content/blog/flutters-path-towards-seamless-interop/images/0-1m66Jxnec3s8Qnb.webp new file mode 100644 index 0000000000..e2a37ac271 Binary files /dev/null and b/sites/www/content/blog/flutters-path-towards-seamless-interop/images/0-1m66Jxnec3s8Qnb.webp differ diff --git a/sites/www/content/blog/flutters-path-towards-seamless-interop/index.md b/sites/www/content/blog/flutters-path-towards-seamless-interop/index.md new file mode 100644 index 0000000000..681d009dcb --- /dev/null +++ b/sites/www/content/blog/flutters-path-towards-seamless-interop/index.md @@ -0,0 +1,50 @@ +--- +title: "Flutter’s path towards seamless interop" +description: "Today, we’re excited to announce an early access program for Flutter plugin authors who are ready to build/re-build a plugin leveraging…" +publishDate: 2025-05-20 +author: mariam_hasnany +category: announcements +layout: blog +--- + +Today, we’re excited to announce [an early access program](https://docs.google.com/forms/d/e/1FAIpQLScSSBAb0sKsrCFJhXnxbhUWx7bdFbuDqWps5C24KIXdD7E-_Q/viewform?usp=header) for Flutter plugin authors who are ready to build/re-build a plugin leveraging the codegen solutions. This is the next phase of development in our work to make it easier for Flutter developers to access native platform APIs by using FFIgen and JNIgen. There are a limited number of slots available by application to those who are ready to engage with us directly on a first wave of production plugins using FFIgen and JNIgen. + +This blog post covers more on what we hope to achieve with direct native interoperability, how we plan to get there, as well as more about the early access program and how you can join us on the journey. + +## The fundamental challenge + +Developers often need to call APIs that are native to the host-platform the app is running on, like iOS or Android. Currently, Flutter developers can do that using method channels. Method channels rely on message passing between Dart code and native code (such asKotlin or Swift) to call the native APIs. That is, Dart code sends native code a message and the native code does something and then responds by sending the result back in a message. + +The fundamental challenge with method channels is that they can be time consuming to implement and maintain. They require a lot of hand-written code. Maintenance requires manually updating their implementation to call the latest API version. They can also be error-prone, as they rely on encoding & decoding messages into strings, which can lead to runtime errors if APIs change. Finally method channels only support asynchronous calls which ensure the UI remains responsive, but can add code complexities in managing async results, handling errors, and dealing with potential latency waiting for results. + +We’ve addressed some of the pain points of using method channels with [Pigeon](https://pub.dev/packages/pigeon), a tool that generates skeleton code for Dart and native APIs to handle messages that you would then manually write with method channels. While Pigeon reduces the time spent wrapping methods and writing message handlers between Dart and native code, APIs with large surface areas still require tedious code to be written. For example, the iOS in-app purchase plugin interacts with a lot of `StoreKit` objects and, since they are nested within other native objects, it takes a lot of manual coding to convert them to Pigeon serializable classes on the Objective-C side and convert those Pigeon classes back to wrapper classes on the Dart side. + +Additionally, interoperability between two plugins is a challenge and there is no way to pass a non-serializable object like an Android `Image` through Dart to another plugin without each of them having a dependency on each other. + +## Flutter’s vision for interoperability + +Like Michael Thompson said during [#FlutterInProduction](https://flutter.dev/events/flutter-in-production), *“We’re investing a lot in a new approach for this, which we call direct native interop.”* + +Our vision for interoperability is to ultimately make accessing platform APIs more effortless for developers with a simple and seamless developer experience. The goal is to enable Flutter developers to focus more time on the experience they are trying to build and less time trying to leverage the power of the underlying platform. + +**The solution** + +We plan on solving the fundamental interop challenge with codegen solutions called FFIgen and JNIgen that handle bridging Dart and native code for each platform directly. FFIgen will generate bindings to wrap Objective-C and Swift APIs, while similarly JNIgen will do the same for Java and Kotlin APIs. + +Unlike method channels, FFIgen and JNIgen will enable APIs to be called synchronously, will support tree-shaking (removal of unused code during compilation), and allow more data to live in the platform layer. + +**The ideal user** + +In this early stage, the ideal users of the new direct interop are Flutter developers who are deeply knowledgeable about native platforms (iOS, Android, or both) with a strong desire to access any platform API they need more directly and with less toil. With FFIgen and JNIgen they can spend significantly less time bridging Dart with native platform code and making it easier to build and maintain plugins for the ecosystem. + +As the tools become more robust over time, we imagine any Flutter developer who is well versed in native platforms will be able to access any platform API (such as one not yet available for Flutter in production or through a package on pub.dev) to generate the code they need to directly call the API with Dart code within their Flutter app. + +Our hope is to land a solution significantly better than what exists today through a simplified developer experience — one that is magical in the right places without forcing developers to give up control. + +## Join Interop Early Access Program + +We are looking for candidates well versed in building Flutter plugins and in using Flutter tools to try an early access program, where you’d rewrite existing plugins to use direct interop, and provide detailed feedback and issues to guide the future development. Like any preview program, there will inevitably be rough edges along the way, but we’re ready to provide support and early access to overcome any obstacles that you experience. + +Indicate your interest by [completing this form](https://docs.google.com/forms/d/e/1FAIpQLScSSBAb0sKsrCFJhXnxbhUWx7bdFbuDqWps5C24KIXdD7E-_Q/viewform?usp=header). We’ll likely not be able to include all who apply to this early program. Selected candidates will hear from us after June 20th, 2025. + +We’re looking forward to hearing from you! \ No newline at end of file diff --git a/sites/www/content/blog/gemini-in-android-studio-now-speaks-fluent-flutter/images/0zs4BmQVfpjV5cawH.webp b/sites/www/content/blog/gemini-in-android-studio-now-speaks-fluent-flutter/images/0zs4BmQVfpjV5cawH.webp new file mode 100644 index 0000000000..a30af2fbc8 Binary files /dev/null and b/sites/www/content/blog/gemini-in-android-studio-now-speaks-fluent-flutter/images/0zs4BmQVfpjV5cawH.webp differ diff --git a/sites/www/content/blog/gemini-in-android-studio-now-speaks-fluent-flutter/images/151UZbL3Qb7BGDEuujTTg6A.gif b/sites/www/content/blog/gemini-in-android-studio-now-speaks-fluent-flutter/images/151UZbL3Qb7BGDEuujTTg6A.gif new file mode 100644 index 0000000000..80a57f415b Binary files /dev/null and b/sites/www/content/blog/gemini-in-android-studio-now-speaks-fluent-flutter/images/151UZbL3Qb7BGDEuujTTg6A.gif differ diff --git a/sites/www/content/blog/gemini-in-android-studio-now-speaks-fluent-flutter/index.md b/sites/www/content/blog/gemini-in-android-studio-now-speaks-fluent-flutter/index.md new file mode 100644 index 0000000000..382ef16eda --- /dev/null +++ b/sites/www/content/blog/gemini-in-android-studio-now-speaks-fluent-flutter/index.md @@ -0,0 +1,69 @@ +--- +title: "Gemini in Android Studio now speaks fluent Flutter!" +description: "By Ander Dobo, Product Manager, Flutter &  +Sandhya Mohan, Product Manager, Gemini in Android Studio" +publishDate: 2025-05-20 +author: anderdobo +image: images/151UZbL3Qb7BGDEuujTTg6A.gif +category: spotlight +layout: blog +--- + +**By Ander Dobo, Product Manager, Flutter & +Sandhya Mohan, Product Manager, Gemini in Android Studio** + +We’re thrilled to announce a major leap forward for Flutter developers creating Android apps in Android Studio. **Gemini in Android Studio now offers first-class support for Dart and Flutter development!** This means that you can leverage the power of Gemini directly within your favorite IDE to build beautiful, high-performance Flutter apps faster and easier than ever before. + +For years, Flutter has empowered developers to create stunning multi-platform experiences from a single codebase. Android Studio has been a cornerstone of that journey, providing a powerful and familiar environment for many Flutter developers. + +We know you’re always looking for ways to boost your productivity and streamline your workflow. You’ve told us you want smarter tools, better code completion, and help tackling those tricky layout challenges. That’s why the Flutter and Android Studio teams partnered to ensure that Gemini understands the unique nuances of Flutter development. This is due to our continued commitment to [making development across platforms easier for developers](https://developers.googleblog.com/en/making-development-across-platforms-easier-for-developers/). + +This isn’t just generic AI assistance. Gemini in Android Studio recognizes Dart syntax, understands Flutter widgets and layouts, and can provide context-aware suggestions tailored specifically to your Flutter projects. Imagine: + +* **Generating UI code with a simple prompt:** Describe the UI you want, and Gemini can generate the corresponding Flutter code. Need a list view with rounded corners and a specific data source? Just ask! + +* **Fixing layout errors instantly:** No more wrestling with overflowing pixels or misaligned widgets. Gemini can analyze your layout, identify the problem, and suggest (or even automatically apply) the fix. + +* **Explaining complex widgets:** Unsure how a particular widget works? Gemini can provide clear explanations and point you to relevant documentation. + +* **Refactoring Dart code with confidence:** Gemini can help you modernize your codebase, improve readability, and apply best practices. + +* **Generating Unit and Widget tests:** Speed up testing with AI-generated tests. + +<DashImage figure src="images/151UZbL3Qb7BGDEuujTTg6A.gif" /> + + +*Explaining Code with Gemini in Android Studio* + +We’ve seen incredible progress in AI’s ability to understand and generate code, and we’re excited to bring these capabilities to the vibrant Flutter community in Android Studio. Our goal is for Gemini to provide relevant, accurate, and helpful assistance throughout the entire development lifecycle. Key features include: + +* **Context-aware chat:** Ask Gemini questions about your Flutter project, and get answers that are tailored to your currently open files. + +* **Smart code completion:** Get intelligent suggestions for Dart code, Flutter widgets, and even pubspec.yaml dependencies. + +* **Actionable insights:** Gemini can analyze your code and provide insights to help you improve performance, identify potential bugs, and follow best practices. + +* **Prompt Library:** Access and create a library of your common Flutter-specific prompts for Gemini in Android Studio. + +<DashImage figure src="images/0zs4BmQVfpjV5cawH.webp" /> + + +*Using the Prompt Library* + +This is just the beginning. We’re committed to continuously improving Gemini’s capabilities and expanding its support for Flutter. We’re actively collecting feedback from the community to guide our future development. + +**Get started for free today!** + +Ready to experience the power of Gemini in Android Studio for Flutter development? + +1. **Download the latest Android Studio:** Make sure you’re running the latest version of Android Studio. + +1. **Enable Gemini:** Open a Flutter project and look for the Gemini icon in the toolbar. Follow the prompts to enable Gemini. + +1. **Start Chatting:** Ask Gemini anything! Experiment with different prompts and see how it can help you. + +1. **Consider Gemini in Android Studio for businesses:** To enable enterprise-grade privacy and security features backed by Google Cloud, you can purchase a Gemini Code Assist Standard or Enterprise license. [Read this blog post](https://android-developers.googleblog.com/2025/04/gemini-in-android-studio-for-business.html) to learn more. + +1. **Give us the gift of feedback:** We want to hear from you! To help us improve, rate the generated output with a thumbs up or thumbs down. If you get an AI response that you feel is unsafe, not helpful, inaccurate, or bad for any other reason, let us know by submitting feedback using the **Provide Feedback** option that appears when you select thumbs down. + +We’re incredibly excited to see what you build with Gemini in Android Studio! \ No newline at end of file diff --git a/sites/www/content/blog/getting-started-with-flutter-gpu/images/074qs6ytcTjyVHwML.webp b/sites/www/content/blog/getting-started-with-flutter-gpu/images/074qs6ytcTjyVHwML.webp new file mode 100644 index 0000000000..e8984a994c Binary files /dev/null and b/sites/www/content/blog/getting-started-with-flutter-gpu/images/074qs6ytcTjyVHwML.webp differ diff --git a/sites/www/content/blog/getting-started-with-flutter-gpu/images/0JEI3fLDGcRHWKruT.webp b/sites/www/content/blog/getting-started-with-flutter-gpu/images/0JEI3fLDGcRHWKruT.webp new file mode 100644 index 0000000000..fb2179ce44 Binary files /dev/null and b/sites/www/content/blog/getting-started-with-flutter-gpu/images/0JEI3fLDGcRHWKruT.webp differ diff --git a/sites/www/content/blog/getting-started-with-flutter-gpu/images/0LWnGU5WPT_Eom0wJ.webp b/sites/www/content/blog/getting-started-with-flutter-gpu/images/0LWnGU5WPT_Eom0wJ.webp new file mode 100644 index 0000000000..fb2179ce44 Binary files /dev/null and b/sites/www/content/blog/getting-started-with-flutter-gpu/images/0LWnGU5WPT_Eom0wJ.webp differ diff --git a/sites/www/content/blog/getting-started-with-flutter-gpu/images/0_-OFc0vhBHAhrPrO.webp b/sites/www/content/blog/getting-started-with-flutter-gpu/images/0_-OFc0vhBHAhrPrO.webp new file mode 100644 index 0000000000..3a21b3603f Binary files /dev/null and b/sites/www/content/blog/getting-started-with-flutter-gpu/images/0_-OFc0vhBHAhrPrO.webp differ diff --git a/sites/www/content/blog/getting-started-with-flutter-gpu/images/0ebUDtzQOuIGmdlop.webp b/sites/www/content/blog/getting-started-with-flutter-gpu/images/0ebUDtzQOuIGmdlop.webp new file mode 100644 index 0000000000..d96b547c89 Binary files /dev/null and b/sites/www/content/blog/getting-started-with-flutter-gpu/images/0ebUDtzQOuIGmdlop.webp differ diff --git a/sites/www/content/blog/getting-started-with-flutter-gpu/images/0hAqIOVkaI1IWnOHE.webp b/sites/www/content/blog/getting-started-with-flutter-gpu/images/0hAqIOVkaI1IWnOHE.webp new file mode 100644 index 0000000000..2740bb0a74 Binary files /dev/null and b/sites/www/content/blog/getting-started-with-flutter-gpu/images/0hAqIOVkaI1IWnOHE.webp differ diff --git a/sites/www/content/blog/getting-started-with-flutter-gpu/images/0lKTtaX2ih6dFpSMQ.webp b/sites/www/content/blog/getting-started-with-flutter-gpu/images/0lKTtaX2ih6dFpSMQ.webp new file mode 100644 index 0000000000..dbaac00177 Binary files /dev/null and b/sites/www/content/blog/getting-started-with-flutter-gpu/images/0lKTtaX2ih6dFpSMQ.webp differ diff --git a/sites/www/content/blog/getting-started-with-flutter-gpu/images/0tC68CbPLef2rJp1e.webp b/sites/www/content/blog/getting-started-with-flutter-gpu/images/0tC68CbPLef2rJp1e.webp new file mode 100644 index 0000000000..956fec4c16 Binary files /dev/null and b/sites/www/content/blog/getting-started-with-flutter-gpu/images/0tC68CbPLef2rJp1e.webp differ diff --git a/sites/www/content/blog/getting-started-with-flutter-gpu/images/0vVWRLxJ348tCxv7T.webp b/sites/www/content/blog/getting-started-with-flutter-gpu/images/0vVWRLxJ348tCxv7T.webp new file mode 100644 index 0000000000..c30eacb733 Binary files /dev/null and b/sites/www/content/blog/getting-started-with-flutter-gpu/images/0vVWRLxJ348tCxv7T.webp differ diff --git a/sites/www/content/blog/getting-started-with-flutter-gpu/images/1jfeUgpEP9AgAz94yVxVW1g.webp b/sites/www/content/blog/getting-started-with-flutter-gpu/images/1jfeUgpEP9AgAz94yVxVW1g.webp new file mode 100644 index 0000000000..267bdf9af5 Binary files /dev/null and b/sites/www/content/blog/getting-started-with-flutter-gpu/images/1jfeUgpEP9AgAz94yVxVW1g.webp differ diff --git a/sites/www/content/blog/getting-started-with-flutter-gpu/index.md b/sites/www/content/blog/getting-started-with-flutter-gpu/index.md new file mode 100644 index 0000000000..19b7189395 --- /dev/null +++ b/sites/www/content/blog/getting-started-with-flutter-gpu/index.md @@ -0,0 +1,803 @@ +--- +title: "Getting started with Flutter GPU" +description: "Build custom renderers and render 3D scenes in Flutter." +publishDate: 2024-08-06 +author: bdero +image: images/1jfeUgpEP9AgAz94yVxVW1g.webp +category: announcements +layout: blog +--- + +The Flutter 3.24 release introduces a new low-level graphics API called [Flutter GPU](https://github.com/flutter/engine/blob/main/docs/impeller/Flutter-GPU.md). There is also a 3D rendering library powered by Flutter GPU called [Flutter Scene](https://pub.dev/packages/flutter_scene) (package: `flutter_scene`). Both Flutter GPU and Flutter Scene are currently in preview, only available on Flutter’s [main channel](https://docs.flutter.dev/release/upgrade#other-channels) (due to reliance on experimental features), require [Impeller to be enabled](https://docs.flutter.dev/perf/impeller#availability), and might occasionally introduce breaking changes. + +This article contains two “getting started” guides for these packages: + +1. 🔺 **Advanced:** [Getting started with Flutter GPU](#d558) +If you’re an experienced graphics programmer or you’re interested in low level graphics and want to build renderers from scratch in Flutter, then this guide will get you set up to start tinkering with Flutter GPU. You’ll draw your first triangle from scratch… in Flutter! + +1. 💚 **Intermediate:** [3D rendering with Flutter Scene](#6b35) +****If you’re a Flutter developer that wants to add 3D functionality to your apps, or you want to create 3D games using Dart and Flutter, then this is a guide for you! You’ll set up a project that imports and renders 3D assets in Flutter. + +## Getting started with Flutter GPU + +⚠️ Warning! ⚠️ Flutter GPU is ultimately a low-level API. It’s overwhelmingly likely that the vast majority of Flutter devs who will benefit from Flutter GPU’s existence will do so by consuming higher level rendering libraries published on pub.dev, such as the Flutter Scene rendering package. If you’re not interested in the Flutter GPU API itself and you’re just interested in 3D rendering, skip ahead to [3D rendering with Flutter Scene](#6b35). + +<DashImage figure src="images/0hAqIOVkaI1IWnOHE.webp" alt="Ooh shiny. This is a ray-marched signed distance field. You could render this using Flutter GPU, but it’s perfectly possible to do so with a [custom fragment shader](https://docs.flutter.dev/ui/design/graphics/fragment-shaders) as well." caption="Ooh shiny. This is a ray-marched signed distance field. You could render this using Flutter GPU, but it’s perfectly possible to do so with a [custom fragment shader](https://docs.flutter.dev/ui/design/graphics/fragment-shaders) as well." /> + + +## Getting started with Flutter GPU + +Flutter GPU is Flutter’s built-in low-level graphics API. It allows you to build and integrate custom renderers in Flutter by writing Dart code and GLSL shaders. No native platform code required. + +Currently, Flutter GPU is in early preview and offers a basic rasterization API, but more functionality will continue to be added and refined, as the API approaches stable. + +Flutter GPU also requires [Impeller to be enabled](https://docs.flutter.dev/perf/impeller#availability). This means it can only be used when targeting platforms that are supported by Impeller. At the time of writing, Impeller supports: + +* iOS (on by default) + +* macOS (opt-in preview) + +* Android (opt-in preview) + +Our aim with Flutter GPU is to eventually support all of Flutter’s platform targets. The ultimate goal is to foster an ecosystem of cross-platform rendering solutions in Flutter that are easy to maintain for package authors and easy to install for users. + +3D rendering is just one possible use case. Flutter GPU can also be used to build specialized 2D renderers, or to do something more unorthodox, like render 3D slices of a 4D space, or project non-euclidean spaces. + +One example of a great use case for a custom 2D renderer powered by Flutter GPU would be 2D character animation formats that rely on skeletal mesh deformation. Spine 2D is a good example of this. Such skeletal mesh solutions usually have animation clips that manipulate translation, rotation, and scale properties of bones in a hierarchy, and each vertex has a few associated “bone weights” that determine what bones should influence the vertex and by how much.’ + +With a Canvas solution like `drawVertices`, bone weight transforms would need to be applied for each vertex on the CPU. With Flutter GPU, the bone transforms could be fed to a vertex shader in the form of a uniform array or even a texture sampler, allowing the final position for each vertex to be calculated in parallel on the GPU based on the state of the skeleton and the per-vertex bone weights. + +With that out of the way, let’s get started with Flutter GPU by way of a gentle introduction: Drawing your first triangle! + +<DashImage figure src="images/0JEI3fLDGcRHWKruT.webp" /> + + +## Add Flutter GPU to your project + +First, note that Flutter GPU is currently in an early preview state and might be prone to API breakages. Quite a lot is already possible with the current API, but experienced graphics engineers might notice some missing common functionality. Much is planned for Flutter GPU over the coming months. + +For these reasons, it’s strongly recommended that, for now, you operate against the tip of the [main channel](https://docs.flutter.dev/release/upgrade#other-channels) when developing packages against Flutter GPU. If you happen to run into any unexpected behavior, bugs, or have feature requests, please file issues using the standard [Flutter issue templates](https://github.com/flutter/flutter/issues/new/choose) on GitHub. All tracked issues related to Flutter GPU are given the [flutter-gpu label](https://github.com/flutter/flutter/labels/flutter-gpu). + +So before experimenting with Flutter GPU, switch Flutter over to the main channel by running the following commands. + +```bash +flutter channel main +flutter upgrade +``` + + +Now create a fresh Flutter project. + +```bash +flutter create my_cool_renderer +cd my_cool_renderer +``` + + +Next, add the flutter_gpu SDK package to your pubspec. + +```bash +flutter pub add flutter_gpu --sdk=flutter +``` + + +## Build and import shader bundles. + +In order to render anything with Flutter GPU, you’ll need to author some GLSL shaders. Flutter GPU’s shaders have different semantics than those consumed by Flutter’s [fragment shader](https://docs.flutter.dev/ui/design/graphics/fragment-shaders) feature, particularly when it comes to uniform bindings. You’ll also define a vertex shader to go alongside the fragment shader. + +Start with defining the simplest possible shaders. You can place shaders anywhere in your project, but for this example, create a `shaders` directory and populate it with two shaders: `simple.vert` and `simple.frag` . + +```c +// Copy into: shaders/simple.vert + +in vec2 position; + +void main() { + gl_Position = vec4(position, 0.0, 1.0); +} +``` + + +When drawing the triangle, you’ll have a list of data that defines each vertex. In this case, it merely lists 2D positions. For each of these vertices, a simple vertex shader assigns these 2D positions to the clip space output intrinsic `gl_Position`. + +```c +// Copy into: shaders/simple.frag + +out vec4 frag_color; + +void main() { + frag_color = vec4(0, 1, 0, 1); +} +``` + + +The fragment shader is even simpler; it outputs an RGBA color with a range of `(0, 0, 0, 0)` to `(1, 1, 1, 1)`. So everything will be shaded as green. + +Okay, so now that you have your shaders, compile them using Flutter's ahead of time (AOT) shader compiler . To set up automated builds for shader bundles, we recommend using the [flutter_gpu_shaders](https://pub.dev/packages/flutter_gpu_shaders) package. + +Use pub to add `flutter_gpu_shaders` as a regular dependency in your project. + +```csharp +flutter pub add flutter_gpu_shaders +``` + + +Flutter GPU shaders are bundled into `.shaderbundle` files, which can be added to your project's asset bundle as regular assets. Shader bundles contain the compiled shader sources for the platform target(s). + +Next, create a shader bundle manifest file that describes the contents of the shader bundle. Add the following to `my_renderer.shaderbundle.json` in the root directory of your project. + +```json +{ + "SimpleVertex": { + "type": "vertex", + "file": "shaders/simple.vert" + }, + "SimpleFragment": { + "type": "fragment", + "file": "shaders/simple.frag" + } +} +``` + + +Each entry in the shader bundle can have an arbitrary name. In this case, the names are "SimpleVertex" and "SimpleFragment". These names are used to look up the shaders in your app. + +Next, use the `flutter_gpu_shaders` package to build the shaderbundle. You can add a hook that automatically triggers a build by enabling the experimental "native assets" feature. Use the following commands to enable native assets and install the `native_assets_cli` package. + +```bash +flutter config --enable-native-assets +flutter pub add native_assets_cli +``` + + +With the native assets feature enabled, add a `build.dart` script under the `hook` directory that will trigger building the shader bundle automatically. + +```dart +// Copy into: hook/build.dart + +import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:flutter_gpu_shaders/build.dart'; + +void main(List<String> args) async { + await build(args, (config, output) async { + await buildShaderBundleJson( + buildConfig: config, + buildOutput: output, + manifestFileName: 'my_renderer.shaderbundle.json'); + }); +} +``` + + +After this change, when the Flutter tool is building the project, `buildShaderBundleJson` builds the shader bundle and outputs the result to `build/shaderbundles/my_renderer.shaderbundle` under the package root. + +The shader bundle format itself is tied to the specific version of Flutter you're using and might change over time. If you're authoring a package that builds shader bundles, do **not** check the generated `.shaderbundle` files into your source tree. Instead, use a build hook to automate the build process (as previously explained). + +This way, developers that use your library will always build fresh shader bundles with the correct format! + +Now that you're automatically building your shader bundle, import it like a regular asset. Add an asset entry to your project's `pubspec.yaml`: + +```yaml +flutter: + assets: + - build/shaderbundles/ +``` + + +In the future, the native assets feature will allow build hooks to append data assets to the bundle. Once this happens, it will no longer be necessary to add an asset import rule alongside the build hook. + +Next, add some code to load up the shaders at runtime. Create `lib/shaders.dart` and add the following code. + +```dart +// Copy into: lib/shaders.dart + +import 'package:flutter_gpu/gpu.dart' as gpu; + +const String _kShaderBundlePath = + 'build/shaderbundles/my_renderer.shaderbundle'; +// NOTE: If you're building a library, the path must be prefixed +// with a package name. For example: +// 'packages/my_cool_renderer/build/shaderbundles/my_renderer.shaderbundle' + +gpu.ShaderLibrary? _shaderLibrary; +gpu.ShaderLibrary get shaderLibrary { + if (_shaderLibrary != null) { + return _shaderLibrary!; + } + _shaderLibrary = gpu.ShaderLibrary.fromAsset(_kShaderBundlePath); + if (_shaderLibrary != null) { + return _shaderLibrary!; + } + + throw Exception("Failed to load shader bundle! ($_kShaderBundlePath)"); +} +``` + + +This code creates a singleton getter for the Flutter GPU shader runtime library. The first time `shaderLibrary` is accessed, the runtime shader library is initialized using the built asset bundle with `gpu.ShaderLibrary.fromAsset(shader_bundle_path)`. + +The project is now set up to use Flutter GPU shaders. It’s time to render that triangle! + +## Drawing your first triangle + +For this guide, you’ll create an RGBA Flutter GPU `Texture` and a `RenderPass` that attaches the Texture as a color output. Then, you’ll render the `Texture` in a widget using [`Canvas.drawImage`](https://api.flutter.dev/flutter/dart-ui/Canvas/drawImage.html). + +For the sake of brevity, you’ll forego best practice and just rebuild all of the resources for each frame. + +As long as you mark your `Texture` as "shader readable" when allocating it, you’ll be able to convert it to a `dart:ui.Image`. To display the rendered results in the widget tree, draw it to a `dart:ui.Canvas`! + +You can access a `Canvas` by scaffolding the widget tree with a custom painter. Replace the contents of `lib/main.dart` with the following: + +```dart +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_gpu/gpu.dart' as gpu; + +// NOTE: We made this earlier while setting up shader bundle imports! +import 'shaders.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter GPU Triangle Example', + home: CustomPaint( + painter: TrianglePainter(), + ), + ); + } +} + +class TrianglePainter extends CustomPainter { + @override + void paint(Canvas canvas, Size size) { + // Attempt to access `gpu.gpuContext`. + // If Flutter GPU isn't supported, an exception will be thrown. + print('Default color format: ' + + gpu.gpuContext.defaultColorFormat.toString()); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => true; +} +``` + + +Now, run the app. As a reminder, Flutter GPU currently requires [Impeller to be enabled](https://docs.flutter.dev/perf/impeller#availability). So you must use an Impeller-supported platform. For this guide, I'll be targeting macOS. + +```bash +flutter run -d macos --enable-impeller +``` + + +<DashImage figure src="images/0lKTtaX2ih6dFpSMQ.webp" /> + + +If Flutter GPU is working, then you should see the default color format printed to the console. + +``` +flutter: Default color format: PixelFormat.b8g8r8a8UNormInt +``` + + +If Impeller isn't enabled, an exception is thrown when attempting to access `gpu.gpuContext`. + +``` +Exception: Flutter GPU requires the Impeller rendering backend to be enabled. + +The relevant error-causing widget was: + CustomPaint +``` + + +For the sake of simplicity, you’ll only modify the `paint` method from here onwards. + +To start off, create a Flutter GPU `Texture`, clear it, and then display it by drawing it to the `Canvas`. + +Create a `Texture` the size of the `Canvas`. A `StorageMode` must be chosen. In this case, you’ll mark the `Texture` as `devicePrivate`, because you'll only be using instructions that accesses the texture's memory from the device (GPU). + +```dart +final texture = gpu.gpuContext.createTexture(gpu.StorageMode.devicePrivate, + size.width.toInt(), size.height.toInt())!; +``` + + +If overwriting the texture's data by uploading it from the host (CPU), then use `StorageMode.hostVisible` instead. + +The third available option is `StorageMode.deviceTransient`, which is useful for attachments that don't need to exceed the lifetime of a single `RenderPass` (so they can just exist in tile memory and don't need to be backed by a VRAM allocation). Oftentimes, depth/stencil textures fit this criteria. + +Next, define a `RenderTarget`. Render targets contain a collection of "attachments" that describe a per-fragment memory layout and its setup/teardown behavior at the beginning and end of a `RenderPass`. + +Essentially, a `RenderTarget` is a reusable descriptor for a `RenderPass`. + +For now, define a very simple `RenderTarget` consisting of only one color attachment. + +```dart +final renderTarget = gpu.RenderTarget.singleColor( +gpu.ColorAttachment(texture: texture, clearValue: Colors.lightBlue)); +``` + + +Notice that this code sets the `clearValue` to light blue. Each attachment has a `LoadAction` and a `StoreAction` that determines what should happen to the attachment's ephemeral tile memory at the beginning and end of the pass, respectively. + +By default, color attachments are set to `LoadAction.clear` (which initializes the tile memory to a given color), and `StoreAction.store` (which saves the results to the attached texture's VRAM allocation). + +Now, create a `CommandBuffer`, spawn a `RenderPass` from it using the `RenderTarget` from earlier, and then immediately submit the `CommandBuffer` to clear the texture. + +```dart +final commandBuffer = gpu.gpuContext.createCommandBuffer(); +final renderPass = commandBuffer.createRenderPass(renderTarget); +// ... draw calls will go here! +commandBuffer.submit(); +``` + + +All that's left is to draw the initialized texture to the Canvas! + +```dart +final image = texture.asImage(); +canvas.drawImage(image, Offset.zero, Paint()); +``` + + +<DashImage figure src="images/0ebUDtzQOuIGmdlop.webp" /> + + +Now that you have a `RenderPass` hooked up with results displayed to the screen, you’re ready to start drawing the triangle. To do this, set up the following: + +1. A `RenderPipeline` created from our shaders, and + +1. A GPU-accessible buffer containing our geometry (three vertex positions). + +Creating the `RenderPipeline` is easy. You just need to combine a vertex and fragment shader from your library. + +```dart +final vert = shaderLibrary['SimpleVertex']!; +final frag = shaderLibrary['SimpleFragment']!; +final pipeline = gpu.gpuContext.createRenderPipeline(vert, frag); +``` + + +Now for the geometry. Recall that the "SimpleVertex" shader only has one input: `in vec2 position`. So, to draw the three vertices, you need three sets of two floating point numbers. + +```dart +final vertices = Float32List.fromList([ + -0.5, -0.5, // First vertex + 0.5, -0.5, // Second vertex + 0.0, 0.5, // Third vertex +]); +final verticesDeviceBuffer = gpu.gpuContext + .createDeviceBufferWithCopy(ByteData.sublistView(vertices))!; +``` + + +All that's remaining is to bind the new resources and call `renderPass.draw()` to finish recording the draw call. + +```dart +renderPass.bindPipeline(pipeline); + +final verticesView = gpu.BufferView( + verticesDeviceBuffer, + offsetInBytes: 0, + lengthInBytes: verticesDeviceBuffer.sizeInBytes, +); +renderPass.bindVertexBuffer(verticesView, 3); + +renderPass.draw(); +``` + + +If you launch your app, you should now see a green triangle! + +<DashImage figure src="images/0LWnGU5WPT_Eom0wJ.webp" /> + + +Yay, you built a renderer from scratch with Flutter, Dart, and a little bit of GLSL! + +Whether this is your first time rendering a triangle or you're a seasoned graphics veteran, I hope you'll continue playing with Flutter GPU and check out the packages that we're working on, like Flutter Scene. + +In the future, we hope to publish beginner-friendly codelabs that dive deeper into Flutter GPU's default behavior and best practices. We still haven't talked about the vertex attribute layout, texture binding, uniforms and alignment requirements, pipeline blending, depth and stencil attachments, perspective correction, and so much more! + +Until then, I'd recommend exploring [Flutter Scene](https://github.com/bdero/flutter_scene) as a more comprehensive example of how to use Flutter GPU. + +## 3D rendering with Flutter Scene + +Flutter Scene (package `flutter_scene`) is a new 3D scene graph package powered by Flutter GPU that enables Flutter developers to import animated glTF models and render realtime 3D scenes. + +The intent is to provide a package that makes building interactive 3D apps and games easy in Flutter. + +<DashImage figure src="images/0tC68CbPLef2rJp1e.webp" /> + + +This package started life as a `dart:ui` extension for a 3D renderer written in C++ and built directly into Flutter's native runtime, but it's been rewritten against Flutter GPU with a more flexible interface. + +As with the Flutter GPU API itself, Flutter Scene is currently in an early preview state and requires [Impeller to be enabled](https://docs.flutter.dev/perf/impeller#availability). Flutter Scene generally keeps up to date with breaking changes to Flutter GPU's API, and so it's strongly recommended to use the [main channel](https://docs.flutter.dev/release/upgrade#other-channels) when experimenting with Flutter Scene. + +Next, make an app with Flutter Scene! + +## Set up a Flutter Scene project + +Since it's strongly recommended to use Flutter Scene against the [main channel](https://docs.flutter.dev/release/upgrade#other-channels), start off by switching to it. + +```bash +flutter channel main +flutter upgrade +``` + + +Next, create a fresh Flutter project. + +```bash +flutter create my_3d_app +cd my_3d_app +``` + + +Flutter Scene relies on the experimental "native assets" feature for automatically building shaders. You’ll use native assets in a moment to set up automatic importing of 3D models for Flutter Scene. + +Enable native assets with the following command. + +```bash +flutter config --enable-native-assets +``` + + +And finally, add Flutter Scene as a project dependency. + +You’ll also need to use several `vector_math` constructs while interacting with Flutter Scene's API, so add the `vector_math` package as well. + +```bash +flutter pub add flutter_scene vector_math +``` + + +Next, import a 3D model! + +## Import a 3D model + +First, you need a 3D model to render. For this guide, you’ll use a common [glTF](https://en.wikipedia.org/wiki/GlTF) sample asset: [DamagedHelmet.glb](https://github.com/KhronosGroup/glTF-Sample-Assets/tree/main/Models/DamagedHelmet). Here's what it looks like. + +<DashImage figure src="images/0vVWRLxJ348tCxv7T.webp" alt="The original Damaged Helmet model was created by theblueturtle_ in 2016 (license: [CC BY-NC 4.0 International](https://creativecommons.org/licenses/by-nc/4.0/legalcode)). The converted glTF version was created by ctxwing in 2018 (license: [CC BY 4.0 International](https://creativecommons.org/licenses/by/4.0/legalcode))" caption="The original Damaged Helmet model was created by theblueturtle_ in 2016 (license: [CC BY-NC 4.0 International](https://creativecommons.org/licenses/by-nc/4.0/legalcode)). The converted glTF version was created by ctxwing in 2018 (license: [CC BY 4.0 International](https://creativecommons.org/licenses/by/4.0/legalcode))" /> + + +You can grab it from the [glTF sample assets repository](https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/DamagedHelmet/glTF-Binary/DamagedHelmet.glb) hosted on GitHub. Place DamagedHelmet.glb in your project root. + +```bash +curl -O https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/main/2.0/DamagedHelmet/glTF-Binary/DamagedHelmet.glb +``` + + +Like most real-time 3D renderers, Flutter Scene uses a specialized 3D model format internally. You can convert standard glTF binaries (.glb files) to this format using Flutter Scene's offline importer tool. + +Add the `flutter_scene_importer` package to the project as a regular dependency. + +```bash +flutter pub add flutter_scene_importer +``` + + +**Note:** The importer will automatically build itself using CMake when invoked. So be sure to [install CMake](https://cmake.org/download/). Otherwise, you will run into “***ProcessException: No such file or directory”*** errors. + +Adding this package makes it possible to invoke the importer manually using `dart run`. + +```java +dart --enable-experiment=native-assets \ + run flutter_scene_importer:import \ + --input "path/to/my/source_model.glb" \ + --output "path/to/my/imported_model.model" +``` + + +You can automatically run the importer by using a native assets build hook. To do this, first install `native_assets_cli` as a regular project dependency. + +```csharp +flutter pub add native_assets_cli +``` + + +You can now write the build hook. Create `hook/build.dart` with the following contents. + +```dart +import 'package:native_assets_cli/native_assets_cli.dart'; +import 'package:flutter_scene_importer/build_hooks.dart'; + +void main(List<String> args) { + build(args, (config, output) async { + buildModels(buildConfig: config, inputFilePaths: [ + 'DamagedHelmet.glb', + ]); + }); +} +``` + + +Using the `buildModels` utility from `flutter_scene_importer`, supply a list of models to build. The paths are relative to the build root of the project. + +When the Flutter tool is building the project, `buildModels` now builds the shader bundle and outputs the result to `build/models/DamagedModel.model` under the package root. + +The imported model format itself is tied to the specific version of Flutter Scene you're using and will change over time. When authoring an app or library that uses Flutter Scene, do not check the generated `.model` files into your source tree. Instead, use a build hook to generate them from your source models (as previously explained). + +This way, you'll always build fresh .model files with the correct format as you upgrade Flutter Scene over time! + +Next, import the model like a regular asset. Add an asset entry to your project's `pubspec.yaml`. + +```yaml +flutter: + assets: + - build/models/ +``` + + +In the future, the native assets feature will allow build hooks to append data assets to the bundle. Once this happens, it will no longer be necessary to add an asset import rule alongside the build hook. + +## Rendering a 3D scene + +Now for the app's code. + +First, create a stateful widget to persist a `Scene` across frames. + +You’ll be animating based on time, so add the `SingleTickerProviderStateMixin` to the state along with an `elapsedSeconds` member. + +```dart +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter_scene/camera.dart'; +import 'package:flutter_scene/node.dart'; +import 'package:flutter_scene/scene.dart'; +import 'package:vector_math/vector_math.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget{ + const MyApp({super.key}); + + @override + MyAppState createState() => MyAppState(); +} + +class MyAppState extends State<MyApp> with SingleTickerProviderStateMixin { + double elapsedSeconds = 0; + Scene scene = Scene(); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'My 3D app', + home: Placeholder(), + ); + } +} +``` + + +Run the app as a smoke test to ensure there are no errors. And remember to [enable Impeller](https://docs.flutter.dev/perf/impeller#availability)! + +```css +flutter run -d macos --enable-impeller +``` + + +<DashImage figure src="images/074qs6ytcTjyVHwML.webp" /> + + +Before continuing, set up the ticker for the animation. Override `initState` in `MyAppState` to call `createTicker`. + +```dart + @override + void initState() { + createTicker((elapsed) { + setState(() { + elapsedSeconds = elapsed.inMilliseconds.toDouble() / 1000; + }); + }).start(); + + super.initState(); + } +``` + + +As long as the widget is visible, the ticker callback is invoked for every frame. Calling `setState` triggers this widget to rebuild every frame. + +Next, load up the 3D model that you placed in the project earlier and add it to the Scene. + +Use `Node.fromAsset` to load the model from the asset bundle. Place the following code in `initState`. + +```dart + Node.fromAsset('build/models/DamagedHelmet.model').then((model) { + model.name = 'Helmet'; + scene.add(model); + }); +``` + + +`Node.fromAsset` asynchronously deserializes the model from the asset bundle and resolves the returned `Future<Node>` once it's ready to be added to the scene. + +The `MyAppState.initState` should now look as follows: + +```dart + @override + void initState() { + createTicker((elapsed) { + setState(() { + elapsedSeconds = elapsed.inMilliseconds.toDouble() / 1000; + }); + }).start(); + + Node.fromAsset('build/models/DamagedHelmet.model').then((model) { + model.name = 'Helmet'; + scene.add(model); + }); + + super.initState(); + } +``` + + +However, you’re still not actually rendering the 3D Scene yet! To do this, use `Scene.render`, which takes a UI `Canvas`, a Flutter Scene `Camera`, and a size. + +One way to access a Canvas is to create a `CustomPainter`: + +```dart +class ScenePainter extends CustomPainter { + ScenePainter({required this.scene, required this.camera}); + Scene scene; + Camera camera; + + @override + void paint(Canvas canvas, Size size) { + scene.render(camera, canvas, viewport: Offset.zero & size); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => true; +} +``` + + +Don't forget to set the `shouldRepaint` override to return true so that the custom painter will repaint whenever a rebuild occurs. + +Lastly, add the `CustomPainter` to the source tree. + +```dart + @override + Widget build(BuildContext context) { + final painter = ScenePainter( + scene: scene, + camera: PerspectiveCamera( + position: Vector3(sin(elapsedSeconds) * 3, 2, cos(elapsedSeconds) * 3), + target: Vector3(0, 0, 0), + ), + ); + + return MaterialApp( + title: 'My 3D app', + home: CustomPaint(painter: painter), + ); + } +``` + + +This code instructs the camera to move in a continuous circle, but always facing towards the origin. + +Finally, start the app! + +```bash +flutter run -d macos --enable-impeller +``` + + +<DashImage figure src="images/0_-OFc0vhBHAhrPrO.webp" /> + + +Here's the full source we put together. + +```dart +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter_scene/camera.dart'; +import 'package:flutter_scene/node.dart'; +import 'package:flutter_scene/scene.dart'; +import 'package:vector_math/vector_math.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({super.key}); + + @override + MyAppState createState() => MyAppState(); +} + +class MyAppState extends State<MyApp> with SingleTickerProviderStateMixin { + double elapsedSeconds = 0; + Scene scene = Scene(); + + @override + void initState() { + createTicker((elapsed) { + setState(() { + elapsedSeconds = elapsed.inMilliseconds.toDouble() / 1000; + }); + }).start(); + + Node.fromAsset('build/models/DamagedHelmet.model').then((model) { + model.name = 'Helmet'; + scene.add(model); + }); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + final painter = ScenePainter( + scene: scene, + camera: PerspectiveCamera( + position: Vector3(sin(elapsedSeconds) * 3, 2, cos(elapsedSeconds) * 3), + target: Vector3(0, 0, 0), + ), + ); + + return MaterialApp( + title: 'My 3D app', + home: CustomPaint(painter: painter), + ); + } +} + +class ScenePainter extends CustomPainter { + ScenePainter({required this.scene, required this.camera}); + Scene scene; + Camera camera; + + @override + void paint(Canvas canvas, Size size) { + scene.render(camera, canvas, viewport: Offset.zero & size); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => true; +} +``` + + +## Flutter’s great future ahead + +If you were able to successfully follow one of these guides and get something up and running: Woohoo, congrats! + +Both Flutter GPU and Flutter Scene are very young efforts with limited platform support. But I think someday we'll look back fondly at these humble beginnings. + +With the Impeller effort, the Flutter team took full ownership over the rendering stack because we needed to specialize the renderer towards Flutter's use case. And now we're starting a new chapter in Flutter's history. One where YOU collectively take control over the rendering! + +Flutter Scene started as a C++ component in Impeller alongside the 2D Canvas renderer with a slim `dart:ui` extension. By the time I was building it, I was already aware that Flutter Engine wouldn't be its final destination. + +The sea of architecture decisions for 3D renderers is vast, and no single generic 3D renderer can solve every use case well. "Generic" and "high performance" are generally antithetical goals. + +At best, adequacy at everything all but guarantees excellence at nothing. + +In the world of rendering performance, the situation is even worse. Specializing for one use case often means degrading the performance of another. + +In short, it's just not possible to ship a generic 3D renderer that can solve every use case for everyone. But, by surfacing the low level APIs necessary for you to build your own solutions (Flutter GPU), and building a useful generic 3D renderer on top of it that's easy for the Flutter community to inspect and modify (Flutter Scene), we’re carving out the space for Flutter developers to enjoy low-risk for obsolescence and a high reward. + +<DashImage figure src="images/1jfeUgpEP9AgAz94yVxVW1g.webp" /> + + +I can't wait to see what you'll make with these new capabilities. Stay tuned for future releases of Flutter Scene. There's a lot on the way. + +In the meantime, I’m heading back to work. + +See you soon. :) \ No newline at end of file diff --git a/sites/www/content/blog/getting-to-the-bottom-of-navigation-in-flutter/images/12_97cfLZr3d3sp2Sgv71Lg.webp b/sites/www/content/blog/getting-to-the-bottom-of-navigation-in-flutter/images/12_97cfLZr3d3sp2Sgv71Lg.webp new file mode 100644 index 0000000000..6f0a822c5b Binary files /dev/null and b/sites/www/content/blog/getting-to-the-bottom-of-navigation-in-flutter/images/12_97cfLZr3d3sp2Sgv71Lg.webp differ diff --git a/sites/www/content/blog/getting-to-the-bottom-of-navigation-in-flutter/images/1SWOS-PvXgnH9-0SWcPt2mA.webp b/sites/www/content/blog/getting-to-the-bottom-of-navigation-in-flutter/images/1SWOS-PvXgnH9-0SWcPt2mA.webp new file mode 100644 index 0000000000..6de3d5292d Binary files /dev/null and b/sites/www/content/blog/getting-to-the-bottom-of-navigation-in-flutter/images/1SWOS-PvXgnH9-0SWcPt2mA.webp differ diff --git a/sites/www/content/blog/getting-to-the-bottom-of-navigation-in-flutter/images/1TIqGBqqOQsvHpUC5kx8P2w.webp b/sites/www/content/blog/getting-to-the-bottom-of-navigation-in-flutter/images/1TIqGBqqOQsvHpUC5kx8P2w.webp new file mode 100644 index 0000000000..6e499f1a31 Binary files /dev/null and b/sites/www/content/blog/getting-to-the-bottom-of-navigation-in-flutter/images/1TIqGBqqOQsvHpUC5kx8P2w.webp differ diff --git a/sites/www/content/blog/getting-to-the-bottom-of-navigation-in-flutter/images/1VLSvnXBbA3ohu3YgpGVIhA.webp b/sites/www/content/blog/getting-to-the-bottom-of-navigation-in-flutter/images/1VLSvnXBbA3ohu3YgpGVIhA.webp new file mode 100644 index 0000000000..f296697e57 Binary files /dev/null and b/sites/www/content/blog/getting-to-the-bottom-of-navigation-in-flutter/images/1VLSvnXBbA3ohu3YgpGVIhA.webp differ diff --git a/sites/www/content/blog/getting-to-the-bottom-of-navigation-in-flutter/images/1pzfLG6bpSs8KGe48AXZR_g.webp b/sites/www/content/blog/getting-to-the-bottom-of-navigation-in-flutter/images/1pzfLG6bpSs8KGe48AXZR_g.webp new file mode 100644 index 0000000000..1f95586f89 Binary files /dev/null and b/sites/www/content/blog/getting-to-the-bottom-of-navigation-in-flutter/images/1pzfLG6bpSs8KGe48AXZR_g.webp differ diff --git a/sites/www/content/blog/getting-to-the-bottom-of-navigation-in-flutter/index.md b/sites/www/content/blog/getting-to-the-bottom-of-navigation-in-flutter/index.md new file mode 100644 index 0000000000..ed4bb9f9c4 --- /dev/null +++ b/sites/www/content/blog/getting-to-the-bottom-of-navigation-in-flutter/index.md @@ -0,0 +1,360 @@ +--- +title: "Getting to the Bottom of Navigation in Flutter" +description: "Bottom navigation in mobile apps is popular because our phones keep getting bigger but our thumbs do not. Here’s how to do it in Flutter." +publishDate: 2019-06-27 +author: hansmuller +image: images/1pzfLG6bpSs8KGe48AXZR_g.webp +category: tutorial +layout: blog +--- + +Bottom navigation in mobile apps is popular because our phones keep getting bigger but our thumbs do not. The Material Design specification describes a [*bottom navigation bar*](https://material.io/design/components/bottom-navigation.html#) as a row of three to five buttons at the bottom of the screen that are used to select among as many *destinations*. + +<DashImage figure src="images/1pzfLG6bpSs8KGe48AXZR_g.webp" alt="A typical bottom navigation bar; Flutter’s BottomNavigationBar widget." caption="A typical bottom navigation bar; Flutter’s BottomNavigationBar widget." /> + + +Flutter provides a [BottomNavigationBar](https://api.flutter.dev/flutter/material/BottomNavigationBar-class.html) widget that’s essentially just a row of destination buttons and a single callback that’s passed the index of the button that was tapped. All the app has to do is to rebuild with a “destination view” widget that corresponds to the button the user tapped. Easy. There’s even a short example that demonstrates as much in the API docs. We’re done right? + +If only it were that simple. + +## Stateful Destination Views + +Destination views are likely to be stateful. They may contain text fields, selection controls, scrollables, or other widgets that depend on state which should not be discarded when the user selects a different destination. In other words, when the user returns to a destination, the destination view should be just as they left it. + +A trivial implementation of the bottom navigation, where only the selected destination view is part of the widget tree, will not retain the state of the other destination views. The simplest way to ensure that the state for all views is retained, is to keep all of the views in the widget tree, while only showing the selected view. Fortunately, there’s a widget for that. + +An [*IndexedStack*](https://api.flutter.dev/flutter/widgets/IndexedStack-class.html) displays only one of its children. All of its children are always part of the widget tree so their state is never discarded. + +To demonstrate using an IndexedStack for displaying the selected destination, I’ve created a few small support classes and variables. + +The *Destination* class contains a few visual properties that identify one destination. There’s also a list of the app’s four destinations, *allDestinations*. + +```dart +class Destination { + const Destination(this.title, this.icon, this.color); + final String title; + final IconData icon; + final MaterialColor color; +} + +const List<Destination> allDestinations = <Destination>[ + Destination('Home', Icons.home, Colors.teal), + Destination('Business', Icons.business, Colors.cyan), + Destination('School', Icons.school, Colors.orange), + Destination('Flight', Icons.flight, Colors.blue) +]; + +``` + +Each stateful *DestinationView* contains a *TextField* to demonstrate that the keyboard focus and the text field’s value persist upon switching destinations. +Each DestinationView has its own scaffold. Not to put too fine a point on it: we’re nesting scaffolds here. That’s OK. + +```dart +class DestinationView extends StatefulWidget { + const DestinationView({ Key key, this.destination }) : super(key: key); + + final Destination destination; + + @override + _DestinationViewState createState() => _DestinationViewState(); +} + +class _DestinationViewState extends State<DestinationView> { + TextEditingController _textController; + + @override + void initState() { + super.initState(); + _textController = TextEditingController( + text: 'sample text: ${widget.destination.title}', + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('${widget.destination.title} Text'), + backgroundColor: widget.destination.color, + ), + backgroundColor: widget.destination.color[100], + body: Container( + padding: const EdgeInsets.all(32.0), + alignment: Alignment.center, + child: TextField(controller: _textController), + ), + ); + } + + @override + void dispose() { + _textController.dispose(); + super.dispose(); + } +} + +``` + +Finally the point of this whole exercise is revealed: the app’s *HomePage,* featuring a scaffold with a *BottomNavigationBar* for the destinations, and an *IndexedStack* for the destination views. As you can see, tapping on a destination (on a *BottomNavigationBarItem*) causes the home page to rebuild with a new value for*_currentIndex*. The indexed stack displays the destination view selected by the current index. + +```dart +class HomePage extends StatefulWidget { + @override + _HomePageState createState() => _HomePageState(); +} + +class _HomePageState extends State<HomePage> with TickerProviderStateMixin<HomePage> { + int _currentIndex = 0; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + top: false, + child: IndexedStack( + index: _currentIndex, + children: allDestinations.map<Widget>((Destination destination) { + return DestinationView(destination: destination); + }).toList(), + ), + ), + bottomNavigationBar: BottomNavigationBar( + currentIndex: _currentIndex, + onTap: (int index) { + setState(() { + _currentIndex = index; + }); + }, + items: allDestinations.map((Destination destination) { + return BottomNavigationBarItem( + icon: Icon(destination.icon), + backgroundColor: destination.color, + title: Text(destination.title) + ); + }).toList(), + ), + ); + } +} + +``` + +And that’s that, you now have bottom navigation with stateful destination views. Everyone is happy. + +<DashImage figure src="images/1VLSvnXBbA3ohu3YgpGVIhA.webp" /> + + +Actually, everyone is not happy. The Material Design spec makes it clear that destination views should cross-fade into view. The indexed stack demo unceremoniously snaps the selected destination into view within a single frame. The spec also suggests that if a destination view is scrollable, the bottom navigation bar should optionally slide off screen when the user scrolls up, and only reappear when the user scrolls down. And finally there’s navigation: many apps require each destination view to be hosted by its own [Navigator](https://api.flutter.dev/flutter/widgets/Navigator-class.html), so that the view can display a stack of routes rather than a single interactive page. + +The following sections will make everyone happy. Happier. + +## Navigator per Destination View + +A Flutter Navigator manages a stack of Route objects and a stack of overlays that are displayed on top. Routes aren’t widgets, they’re objects that have-a widget. A route’s widget can be a fully opaque page or a smaller user interface part like a dialog or a menu. + +A complete discussion of navigators and routes is beyond the scope of this article. To help you understand the version of the demo where each destination view includes a navigator, here are a few facts to keep in mind: + +* Navigators have push and pop methods for managing the stack of routes. + +* Navigators support lazily creating routes. Lazily created routes are identified by a path name. + +* By default the navigator displays the route called ‘/’. + +* Any widget can push or pop routes on its navigator ancestor with the static [Navigator.push](https://api.flutter.dev/flutter/widgets/Navigator/push.html) and [Navigator.pop](https://api.flutter.dev/flutter/widgets/Navigator/pop.html) methods. + +The code that follows is a version of the destination view’s state which builds a navigator instead of just building a text field. Routes are lazily constructed by the navigator’s *onGenerateRoute* callback and each route’s widget is constructed by a *MaterialPageRoute builder* callback. + +```dart +class _DestinationViewState extends State<DestinationView> { + @override + Widget build(BuildContext context) { + return Navigator( + onGenerateRoute: (RouteSettings settings) { + return MaterialPageRoute( + settings: settings, + builder: (BuildContext context) { + switch(settings.name) { + case '/': + return RootPage(destination: widget.destination); + case '/list': + return ListPage(destination: widget.destination); + case '/text': + return TextPage(destination: widget.destination); + } + }, + ); + }, + ); + } +} + +``` + +The *RootPage* widget handles taps by pushing the route called ‘/list’ that contains a ListPage widget: + +```dart +onTap: () { + Navigator.pushNamed(context, "/list"); +}, + +``` + +The *ListPage* widget is similar. Tapping any list item causes it to push a ‘/text’ route which will contain a *TextPage* widget. The TextPage widget is essentially the same as the original DestinationView (it contains a single TextField). + +<DashImage figure src="images/1SWOS-PvXgnH9-0SWcPt2mA.webp" /> + + +## Cross Fading Destination Views + +The basic idea here is pretty simple: stack the destination views, fade in the selected view, and fade out the unselected view[s]. Once a destination has faded out we’ll move it [*Offstage*](https://api.flutter.dev/flutter/widgets/Offstage-class.html) so that it’s no longer rendered or hit tested. To ensure that moving views offstage preserves their state, give them a Global Key. + +A brief word about global keys and preserving a widget’s state. + +Each time the widget tree is rebuilt, Flutter preserves the state of widgets that occupy the same place in the tree, and have the same key as they did in the previous frame. Most widgets are created without a key, so this test simplifies to: same runtime type and same tree location. A widget with a global key is treated differently. Its state and its subtree are *moved* if the keyed widget has moved to a new location when the tree rebuilds. + +This version of the demo requires some additional HomePage state for each destination view: a fader animation and a global key. + +```dart +List<AnimationController> _faders; +List<Key> _destinationKeys; +int _currentIndex = 0; + +@override +void initState() { + super.initState(); + + _faders = allDestinations.map<AnimationController>((Destination destination) { + return AnimationController(vsync: this, duration: Duration(milliseconds: 200)); + }).toList(); + _faders[_currentIndex].value = 1.0; + _destinationKeys = List<Key>.generate(allDestinations.length, (int index) => GlobalKey()).toList(); +} + +@override +void dispose() { + for (AnimationController controller in _faders) + controller.dispose(); + super.dispose(); +} + +``` + +In the cross fading version of the demo, the original indexed stack has been replaced by an ordinary [*Stack*](https://api.flutter.dev/flutter/widgets/Stack-class.html) where each destination view has been wrapped with a [*FadeTransition*](https://api.flutter.dev/flutter/widgets/FadeTransition-class.html) and been assigned a global key with a *[KeyedSubtree](https://api.flutter.dev/flutter/widgets/KeyedSubtree-class.html).* Each fade transition is driven by one of the animation controllers in the list of _faders shown above. + +The fader for the currently selected destination view is driven forwards, which causes it to fade in. The faders for the other views are driven in reverse, and those which have completely faded out are moved offstage. While a view is fading out it’s wrapped with *[IgnorePointer](https://api.flutter.dev/flutter/widgets/IgnorePointer-class.html),* so that it doesn’t respond to user gestures. + +```dart +Stack( + fit: StackFit.expand, + children: allDestinations.map((Destination destination) { + final Widget view = FadeTransition( + opacity: _faders[destination.index].drive(CurveTween(curve: Curves.fastOutSlowIn)), + child: KeyedSubtree( + key: _destinationKeys[destination.index], + child: DestinationView( + destination: destination, + ), + ), + ); + if (destination.index == _currentIndex) { + _faders[destination.index].forward(); + return view; + } else { + _faders[destination.index].reverse(); + if (_faders[destination.index].isAnimating) { + return IgnorePointer(child: view); + } + return Offstage(child: view); + } + }).toList(), +) + +``` + +That’s it for cross fading. Hopefully it’s obvious that it would be easy to substitute a different transition for the fade. + +<DashImage figure src="images/12_97cfLZr3d3sp2Sgv71Lg.webp" /> + + +## Hiding the Bottom Navigation Bar on Scroll + +In this version of the demo, any destination view with scrollable content causes the bottom navigation bar to slide off screen when the content is scrolled downwards, and back on screen when the content scrolls upwards. We’ll use a [*NotificationListener<ScrollNotification>*](https://api.flutter.dev/flutter/widgets/NotificationListener-class.html) at the root of each destination view to detect changes in the scroll direction. Wrapping the bottom navigation bar with a [*SizeTransition*](https://api.flutter.dev/flutter/widgets/SizeTransition-class.html) makes it possible to animate the bar on and off screen. + +This version of the demo requires adding one more animation controller for showing and hiding the bottom navigation bar the destination view’s state: + +```dart +// ... +AnimationController _hide; + +@override +void initState() { + super.initState(); + // ... + _hide = AnimationController(vsync: this, duration: kThemeAnimationDuration); +} + +@override +void dispose() { + // ... + _hide.dispose(); + super.dispose(); +} + +``` + +When the scroll direction changes, the scroll notification’s callback runs the _hide animation controller forward to hide the bottom navigation bar, and in reverse to show it. We use the notification’s *depth* to distinguish the topmost scrollable from nested ones. + +The size transition keeps the child of the navigation bar top-aligned as it grows and shrinks, by specifying axisAlignment as -1. + +```dart +bool _handleScrollNotification(ScrollNotification notification) { + if (notification.depth == 0) { + if (notification is UserScrollNotification) { + final UserScrollNotification userScroll = notification; + switch (userScroll.direction) { + case ScrollDirection.forward: + _hide.forward(); + break; + case ScrollDirection.reverse: + _hide.reverse(); + break; + case ScrollDirection.idle: + break; + } + } + } + return false; +} + +Widget build(BuildContext context) { + return NotificationListener<ScrollNotification>( + onNotification: _handleScrollNotification, + child: Scaffold( + // ... + bottomNavigationBar: SizeTransition( + sizeFactor: _hide, + axisAlignment: -1.0, + child: BottomNavigationBar( + // ... + ), + ), + ), + ); +} + +``` + +That’s just about that for adding support for changing the visibility of the bottom navigation bar when the current destination is scrolled. If you read the source for the [complete demo](https://gist.github.com/HansMuller/b189642d10fd236a41044fdf7626f7b0) you’ll see one additional tweak. Each time a destination view’s navigator pushes or pops a new route, the bottom navigation bar is shown. This is done by giving each destination view navigator a *[NavigatorObserver](https://api.flutter.dev/flutter/widgets/Navigator/observers.html),* which ensures that the bottom navigation bar is visible. + +<DashImage figure src="images/1TIqGBqqOQsvHpUC5kx8P2w.webp" /> + + +## Summary + +This article explains how to create a bottom navigation bar app using Flutter. The effect is similar to tabbed web browsing with the tabs on the bottom: each tab (or “destination”) selects a view that provides a navigation stack. + +In addition to the BottomNavigationBar widget, the [demo implementation](https://gist.github.com/HansMuller/b189642d10fd236a41044fdf7626f7b0) uses the Stack, Navigator, IgnorePointer, and Offstage widgets to manage the destination views, the SizeTransition and FadeTransition widgets for animation, and the NotificationListener and NavigatorObserver widgets to track state changes. + +This article is based on a talk I gave at the [NYC Flutter Meetup on 5/22/2019](https://www.meetup.com/flutter-nyc/events/257815270/). + +Many of the examples depend on Flutter 1.7.1 or a newer version. \ No newline at end of file diff --git a/sites/www/content/blog/global-gamers-challenge-top-20-revealed/images/03a0UYlMSfs-7N9fq.webp b/sites/www/content/blog/global-gamers-challenge-top-20-revealed/images/03a0UYlMSfs-7N9fq.webp new file mode 100644 index 0000000000..ce5faee480 Binary files /dev/null and b/sites/www/content/blog/global-gamers-challenge-top-20-revealed/images/03a0UYlMSfs-7N9fq.webp differ diff --git a/sites/www/content/blog/global-gamers-challenge-top-20-revealed/index.md b/sites/www/content/blog/global-gamers-challenge-top-20-revealed/index.md new file mode 100644 index 0000000000..68113bd528 --- /dev/null +++ b/sites/www/content/blog/global-gamers-challenge-top-20-revealed/index.md @@ -0,0 +1,70 @@ +--- +title: "Global Gamers Challenge: Top 20 Revealed!" +description: "The Global Gamers Challenge is nearing its conclusion, and it’s time to unveil the 20 projects progressing to the final round of judging." +publishDate: 2024-04-04 +author: kkboateng +image: images/03a0UYlMSfs-7N9fq.webp +category: events +layout: blog +--- + +<DashImage figure src="images/03a0UYlMSfs-7N9fq.webp" /> + + +It’s been incredibly inspiring to see thousands of developers work together to develop epic games for the planet with Flutter and Dart over the last 9 weeks. Entries included RPGs, platformers, AI-powered, and more. Your creative ideas spanned continents, genres, formats, platforms, and even galaxies! + +Your creativity never fails to amaze us, so our panel of Flutter judges had some tough decisions to make as they evaluated hundreds of games from talented developers, designers, artists, and activists from every corner of the globe. Without further ado, here are the Top 20! + +## The Big Reveal! + +Drumroll please! Here are the 20 projects moving on to the final round: + +* [Better World](https://globalgamers.devpost.com/review/submissions/enlLZWd3d1lwWTk3b0lTanhXbDcxcmpnRG9ydTBrcWgrVGZmWFI2R0NNMW9hRjI3NHhrdW1LMlBkU1lqWDZJOUQ2WnlSUVJPQ3B3aDRmM20xTTVFMDNHQXdqZmszVHVjM0ZHN2xCdHRBeHc9LS0zOG11OXMxMkU5dVFxSlJQSkJScDdBPT0=--e11a994803647f9d016701bd2cf338a94fb9e4e2) + +* [Bin It Right](https://globalgamers.devpost.com/review/submissions/ZTNIa002Z212RWE1N3VKYXdyK0EyVjVoYUxadFVJOU5HQTZFNk4wNHFMQ1BLbHZCcDBjOGpublh6ZGFjNWdQVGt5azYzT0pmRDhwQXRoaWlRNytjT3BxeUV1UFF4Tm1nWGM5ZWJOV0cxc2c9LS05dS9nWnF0QjV3OGpQSmhsdVdQNUdBPT0=--c4f5738e6bb6c62e468daadcdb4f7984df8b994c) + +* [Craftown](https://globalgamers.devpost.com/review/submissions/eHNnUmpnV0sxWlA5RTlXazNoT0RjTVlldy9iaXJHY1N4Q0NpMFhTRU5vc1hsUGgyTldtZ2hNaEhiS3lmeFZNRGFIdzI2Y3VnRHdzc083dThhalpLY3hUTmZEYXRiNWFxd29wM3hNTHBXbHM9LS1lL0RiWWhlUUxSdDdKZXE5cGJ5UW13PT0=--57d7e84271214ea300127a0e83ea64a1a692d56c) + +* [EcoShift Chronicles](https://globalgamers.devpost.com/review/submissions/V2hQQitXY1FFV2N3TUJWS1FrRTVnUzFYV0hQVWo2Y0dTTU1NdHZWL25iR2VndUFwNkFjWkVXNEJHaXk2NWRrYzl5UDBpSkd3bFVOMDZPWm85ZGxvK203cC9QOXQweVVxbUZ2c3VzNFUwV289LS0xbUJNb25iaFFSRGptT1Z4aW9MTmdRPT0=--4788773139af2e54ffe258b8b1488ad313514fca) + +* [Escape From Heat Island](https://globalgamers.devpost.com/review/submissions/S0lJMkQrSzViVjQxT1Jsa1ZCSkNaaTIzajNUMVI3TEx5c1RsbUxFbTM2dHZjY1l0YUxqM0NMQlViODBCUEN0U2JpVVI0VVhxQ2lxNGpiQjhKanYzanZVd2kyL2tCbjlKeTQ2U0pOVFVNelU9LS1EREF5NFJiT2YyS3JpZVRoR2Ezam5nPT0=--1318da02bb7f68768ff57cc2c4b2f08a73bfeec1) + +* [Gomiland](https://globalgamers.devpost.com/review/submissions/SEpSdVVkb1J5dUFPZGlHcUJtWTI5eWRKM0FUZTVUbUIydTgwWU9qRU5vTlBxOFJIdXNRTGszNEFoQnpxL21uSk4zZUR5RmtCT0U1cVdXVEdOUldDUTlLL2hMWlFKcW92ZmkyRkR6dU1hTFk9LS1oVlpBM2hDWjJ6L25TNFFET0NqcW1nPT0=--b19149b1358c52b67238c81a3b0531d95d1c676a) + +* [Green Capitalist](https://globalgamers.devpost.com/review/submissions/UHZCNC9wUG9Qd2NJakYxMkZ5YTZucjZCNGZVbXZGeWN3b0FXSFphbmYxdkNZWEdyRkllRjIwenB1cW1NVWhrQVFnMmsxSXlNSXJWbUFzVndUS2NoR201TFBJZVkySjVCRndzeURyQThHanc9LS1Yc1JQbkdsZkQxaUM5Y0M5elRVQ1pnPT0=--2f95599fcbeb41f022bde22e42ec6f3d92663fc7) + +* [Last Bottle](https://globalgamers.devpost.com/review/submissions/T1JpMmMxQjU3aG1NRk53OXQzWWZ1b2Vpd3ZrTWZsaFRweVZTQlExOFZkZDlvNkgyUW5tamt6WmNTakh4Zi9lUUFsaTY5Tm42OTNlNGZjYWl3Szk0Y2d6MTlrcll4RXpkc3pzMU53em1IUzQ9LS1MdkFZYnAzVWRKWWdLVE1UMUJDeG5nPT0=--1316df99b2d96029da5b27a13659d9d32010a223) + +* [MGame — A waste management game](https://globalgamers.devpost.com/review/submissions/ZVM2RVBaQlUyTmxoamxPZlByVTdYWmJZeGNYaWNtVDJsZU1kN0F3MzRPaVBBL1VUQTgrOTFoOFVrdmtGdDkxR0pXVThyeTJXdElKVlR2dVJ6aUVRRTFOb3VwV0o1MzJDRHJYNlpVd1U1M3c9LS1NM3B3OTBERWlZSWZPdmovTlpUbUlRPT0=--f2a60f8f9cbc7dfb3f34b6933b09a2091da0e863) + +* [Ocean Ranger](https://globalgamers.devpost.com/review/submissions/aWcvcmo2VExlMjh4QTJwb3Bqbmxma2tsKzhBaDBYdXNuU0N4YTFuL1lWNkI4d1BjZThqdG5wb05uNUFnblZaZTNRY2hVWnJtUUpNTDZCdTdESktuQjlhRGQ4SG9rVURnMU9vbzV1V1l1bGc9LS1hb2w3RjUvQ3ZnTFZqU0Fha1hyTXZBPT0=--3826cbc4d405d18dc05c2fca5a7ea20d6d0d84e7)s + +* [Ofeka Earth](https://globalgamers.devpost.com/review/submissions/dnZ0ZDU5aWJWWFJ3ZkkrNVp2Y3hYeWwvN0ZYSXBSZG0xelhBdG5CZ2d4ZmUzK0JMSFNkSm56S09BOXY2ZHVGS0EwMzJqUzVEVk85V0hJL25pNTRMYThjVE9vR1ZxMThqaS9NL05UUndXU3c9LS1YRElEZTg0ME10ME9NblhqV2hVTXd3PT0=--19f36428769b3c76b639dcc1b1b083a6b8f452fb) + +* [PlasticPunk](https://globalgamers.devpost.com/review/submissions/aFRPak8rVjZGd2t2RGFqb3ZvREpBYnYxM1J1M2ZHWVlNNzZ0ZVIxQ0ZnMXVZYVcvSVlYc2dlbTZ1OXNJTE5DUUpmY2FDbEg1WkZqYVo1eGtrL0dYb1lFVk4zMjQ1SXY0UU1BZkhPUFZYNlU9LS1NTHc5T3NLbnIyVG9wV0puYVlhcEhnPT0=--2cc59a62faacb6e4f070d15ede9dd3cc5e4f1f0b) + +* [Recyclo](https://globalgamers.devpost.com/review/submissions/dldUUUNMSFpjUnVDQ3FzZHJRRldTVVc5dkpvYldDV3V5V0tPODg5WnBtb0Z5ODV6SG9aMGtMNlIzbHdLNVdWUyt4ZGZWWUlybksyZlFDVHJRNDFuc3JuMkRRTW41MGtycnhSaWdncXVIMkE9LS1RNTFtdUFvR1A0ZkxZRTRtdDErN2dBPT0=--4451922054b1b673ba4ab0615c7c968399dbd3bd) + +* [Scuba Sweep](https://globalgamers.devpost.com/review/submissions/TDI1cEVFSVBZelZtR2hsMEVTZWFWYTUwa1FYeldWSDFwdkIvckZKdFhMSUZrRlY4aUlWNVRCQVJPSG1lSWZ3a1liZks2cjVPNmlCSlJEZDg0QXhjU1ZjdVdHN2ZTZjlHV2lMUlRPK2lvTzQ9LS1ESG5wTG9oS2J1UnBsMWNHVWlwbnZ3PT0=--76148d0a4646534a5695130f1db1d728c7f6ac5a) + +* [Sinking Earth (Thinking Us)](https://globalgamers.devpost.com/review/submissions/eVI0blRPZ3R1SkV6MUdtMXNmeGdWU21ONEloV2NwSS81QUtOYWxUZDlPMTI2bG1xTXdoWGFNRTRkWVZQRFV0ejJxbDF6aDRyK2dsM2l6cEhpQmI5ekhLNENmdjVKc1ZKMWpvaVlYWE41YnM9LS1CYXFnMnI2aDRCcVVYL3VVWXdFWUl3PT0=--81ee42d6ae67ee20e733d471a7fd5f9c4fd03ab6) + +* [Socket Tower](https://globalgamers.devpost.com/review/submissions/WFFkRy9XOE0xc0F1NzVpbkVVdzZoSWxzMmM0WlZ0dWpjeXZVUEhmSExKWFhDRExVNFY5Tm9nVkphcVdTa2NPeitoQ09OTElFMUlyYWJ2elN2cm1xWjRtaXZZcXAzdVJJdGRkZE9VVjBmVms9LS1Xc1dhOGRiM2FhUnd2NkpkMy93NDRRPT0=--756045c57682c3a3e663876f4fe0f78a445b1885) + +* [The Shady Story: Recycling](https://globalgamers.devpost.com/review/submissions/bXhaM2xmN3lpSTdkMytjeVpHOVhCdy9LRkdQcFpLeUpvZVJLdEtFbUhYVGtxb0NEbXZxWEdBcm5rSG1yZWtQQ1J4UXNZZXVIZlZGNDlnRms4NUJ5cFhZZ2FuckFOS3Rmai9jTlFIZGRwbHc9LS0wV2lBT29mam90d0xRcjNzNm5Zb1VBPT0=--5118b6c9045e73bfd35050599b959726b1c1a237) + +* [Waste Wise](https://globalgamers.devpost.com/review/submissions/eTN0dGVHRVZLRlZpQndUVG4rL1pVbXhtNnREQWFLeFNTdFNJTzQ3RTRDSDdsNHlsbzFSUHlITW1CQzk1Q1F1QUZ0UWw5Y1paSVlWSUJMUEdLbDZnOFBhQ0QwVWxhRExWOEZHQkx5R0d6Snc9LS1YRHBSM0Q1TUIxalhXK2xsWmd2V0t3PT0=--7f63c9da364e949fd376b8b1777d1c6c44cf627a) + +* [Whaley’s Bins Waste Sorting](https://globalgamers.devpost.com/review/submissions/SWRGWEEzQmZQSkRlSWp3M2VPYmxpMGk0NnhuNEJ0VGtjQ29pSUY3NXRRK1B6VnlRUC85ZytRakpBeW92Zi9HemZRMFhpM2ZOQmFiSEhody9VbHNlR1F1a0FXcWVJWENqVTUzMHlha0lKcGc9LS0yODlkY0J5SHhEdDJLaVNVOG5pS1ZBPT0=--6ebd73c62cc6042aa07aeb529b1d9fa3a6c15ebb) + +* [WildRun](https://globalgamers.devpost.com/review/submissions/ZmNDMlJweVI2cGE3WTNnUUZDK21XSVhTRWUrL1RKQnQ0VTJzT1BpOWRSaFBZSTErK28veTBsbFliSzA4dVJoM1N0Wk8vUnZuQkJnOE5vTDhxMHl6QjNyRURoVll3cVpxcGdVS0UxeGdJYkk9LS0ydnlWNVJOcFFRWWhqc0w4NVRDSkp3PT0=--88495cf408930c8e6cace121f602b55550131898) + +## What’s next? + +It gets even more exciting from here! The final panel of judges, representing Global Citizen, the UN Playing for the Planet Alliance, Google Play, YouTube, and the Flutter community, will choose our 10 finalists. The Top 10 will be announced in May 2024. + +In the meantime, starting April 22nd, you get to vote for your favorites! Head over to [globalgamers.devpost.com](http://globalgamers.devpost.com) to cast your vote and help determine our Community Choice champs! + +## Thank you and keep building! + +Thank you from the bottom of our hearts to everyone who participated, and a huge congrats again to our Top 20! And from all of us at Google and Global Citizen — keep dreaming big, keep coding, and keep fighting for our planet, one amazing game at a time! \ No newline at end of file diff --git a/sites/www/content/blog/going-deeper-with-flutters-web-support/images/0Qvklox5veq6mDZhF.webp b/sites/www/content/blog/going-deeper-with-flutters-web-support/images/0Qvklox5veq6mDZhF.webp new file mode 100644 index 0000000000..d2e5148b87 Binary files /dev/null and b/sites/www/content/blog/going-deeper-with-flutters-web-support/images/0Qvklox5veq6mDZhF.webp differ diff --git a/sites/www/content/blog/going-deeper-with-flutters-web-support/images/0RAaqRbmWskcjj6j2.webp b/sites/www/content/blog/going-deeper-with-flutters-web-support/images/0RAaqRbmWskcjj6j2.webp new file mode 100644 index 0000000000..6a5f91cdb7 Binary files /dev/null and b/sites/www/content/blog/going-deeper-with-flutters-web-support/images/0RAaqRbmWskcjj6j2.webp differ diff --git a/sites/www/content/blog/going-deeper-with-flutters-web-support/images/1cDlTCoUW0SRMVUl4B1sDPw.webp b/sites/www/content/blog/going-deeper-with-flutters-web-support/images/1cDlTCoUW0SRMVUl4B1sDPw.webp new file mode 100644 index 0000000000..3b2a99e822 Binary files /dev/null and b/sites/www/content/blog/going-deeper-with-flutters-web-support/images/1cDlTCoUW0SRMVUl4B1sDPw.webp differ diff --git a/sites/www/content/blog/going-deeper-with-flutters-web-support/images/1dbcXXNRiMmMXxA1YsZYwcQ.webp b/sites/www/content/blog/going-deeper-with-flutters-web-support/images/1dbcXXNRiMmMXxA1YsZYwcQ.webp new file mode 100644 index 0000000000..d4d882f657 Binary files /dev/null and b/sites/www/content/blog/going-deeper-with-flutters-web-support/images/1dbcXXNRiMmMXxA1YsZYwcQ.webp differ diff --git a/sites/www/content/blog/going-deeper-with-flutters-web-support/index.md b/sites/www/content/blog/going-deeper-with-flutters-web-support/index.md new file mode 100644 index 0000000000..58e2a3fcea --- /dev/null +++ b/sites/www/content/blog/going-deeper-with-flutters-web-support/index.md @@ -0,0 +1,110 @@ +--- +title: "Going deeper with Flutter’s web support" +description: "Harnessing the full power of the web platform for app-centric experiences" +publishDate: 2020-07-29 +author: mariam_hasnany +image: images/1dbcXXNRiMmMXxA1YsZYwcQ.webp +category: announcements +layout: blog +--- + +Today we want to give an update on our progress towards bringing Flutter to the web, with a particular focus on work we’re doing to integrate deeply and natively with the web platform. + +<DashImage figure src="images/1dbcXXNRiMmMXxA1YsZYwcQ.webp" /> + + +Our vision for Flutter is to provide a portable toolkit for building beautiful experiences wherever you might want to paint pixels on the screen. As [we’ve noted previously](https://medium.com/flutter/flutter-web-support-updates-8b14bfe6a908), we are deliberately app-centric with our framework and API choices, building a layered architecture that can scale across mobile, desktop, and embedded devices without compromising performance or quality. + +The web is at the core of our work. Flutter was originally born out of explorations from the Chrome team. It draws inspiration from the productivity and iterative development model of the web, and many of our engineers have years of experience building browser engines and web standards. We target the web browser because it is the beating heart of the internet: the most pervasive, flexible, and extensive app model that has existed in computing history. + +For years, the web platform and the HTML DOM were essentially the same thing. But recently, the web platform has expanded with ever-richer APIs and capabilities that expose the underlying operating system and hardware. From [hardware-accelerated graphics](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API) to [shell integration](https://w3c.github.io/manifest/) and [PWAs](https://web.dev/progressive-web-apps/), from [service workers](https://developers.google.com/web/fundamentals/primers/service-workers) to new [layout and paint APIs](https://developer.mozilla.org/en-US/docs/Web/Houdini), the web is an ever more potent target for complex apps and even frameworks that layer on top of this foundation. + +Flutter’s web support is not simply a transpiler to the DOM: it takes full advantage of this emerging set of APIs to offer an experience that is both true to the web and a no-compromise expression of Flutter. + +In this post, we provide a deeper insight into this story, sharing how Flutter uses the web to enable rich interactive experiences without having to rewrite existing Flutter code. We also address areas like accessibility and backend compilation choices that may not be immediately obvious to a casual experimenter. + +## More than a canvas painting + +What does it actually mean to paint Flutter widgets as pixels on the web? + +Let’s start with a recap of Flutter’s web architecture. Flutter is a [multilayer system](https://flutter.dev/docs/resources/technical-overview#layer-cakes-are-delicious) with: + +* a *framework* that provides abstractions for common idioms like widgets, animation and gestures. + +* an *engine* that renders to the target device using the system APIs it exposes. + +The framework code is written in Dart. We combine that with your own code and at compile time apply [tree-shaking algorithms](https://medium.com/flutter/optimizing-performance-in-flutter-web-apps-with-tree-shaking-and-deferred-loading-535fbe3cd674) so that only the code used by your application is downloaded to the browser. Flutter adopts a reactive model, rendering the user interface in response to state changes. Within each frame Flutter builds widgets, performs layout, and lastly paints changes to the UI using the underlying browser APIs. + +<DashImage figure src="images/0RAaqRbmWskcjj6j2.webp" alt="Flutter’s architecture for the web is multilayer system with a Framework on top of a browser engine." caption="Flutter’s architecture for the web is multilayer system with a Framework on top of a browser engine." /> + + +By default, the web mode uses standard HTML DOM and Canvas technologies. In this mode, the engine translates each generated Flutter scene into HTML, CSS or Canvas, and renders a frame onto the page as a tree of HTML elements. + +We call this approach the DomCanvas backend. It gives us the most compatibility across browsers with a compact code size and is well suited for apps with short lived sessions that require fast startup. + +While we continue to make good progress on DomCanvas performance and fidelity, we’re also working in parallel on a [CanvasKit](https://skia.org/user/modules/canvaskit)-based backend that enables rendering Skia paint commands in the browser using [WebAssembly](https://webassembly.org/) and [WebGL](https://www.khronos.org/webgl/). We started experimenting with CanvasKit because Skia is the same graphics engine used by Flutter mobile and desktop and, unlike HTML DOM, it allows direct access to the low-level graphics stack enabling full parity with native Flutter. + +While DomCanvas offers broader browser support, smaller code size, fast initial page load, CanvasKit allows Flutter developers to embrace new technologies for building graphically-intensive web apps. In the future, the advantages of one approach may become overwhelming, but for now Flutter gives you [the option to choose](https://github.com/flutter/flutter/wiki/Experimental-flags-for-web-support#canvaskit-backend) which backend best fits your desired use case. + +## Feeling at home in the browser + +For a web application to feel at home in the browser, it must support a wide variety of browser idioms, including natural text interaction and scrolling behavior, back/forward navigation, and accessibility, all with good performance across a wide variety of devices and form factors. + +### **Text rendering** + +One of the biggest challenges we faced when building Flutter’s web support was developing a layout system specifically for text layout. In order to lay out a paragraph of text, Flutter creates a [`paragraph`](https://api.flutter.dev/flutter/dart-ui/Paragraph-class.html) object and calls its [`layout()`](https://api.flutter.dev/flutter/dart-ui/Paragraph/layout.html) method. Since the web currently lacks a direct text layout API, we use the DOM to perform various measurements on the paragraph by triggering `layout()` and observing side-effects of layout properties for other elements in the frame. + +As you can imagine, these measurements can become quite expensive, so we recently started experimenting with using a [canvas API for measuring text](https://github.com/flutter/flutter/issues/33523) to improve both backend approaches. We’ve found that it improves text layout performance by up to 6x and also resolves several issues with multi-line text fields and text overflow. You can enable these canvas text improvements with a [release mode flag](https://github.com/flutter/flutter/wiki/Experimental-flags-for-web-support#text-rendering-performance-improvements). *(Update as of 7/30/2020: canvas text measurement is now on by default in the `master` channel and will gradually roll out to other channels.)* + +While rendering the text quickly and correctly is important to the end-user experience, being able to interact with the text on the browser is just as important. Today you can make text selectable on a Flutter web app by using either the `SelectableText` or `EditableText` widgets. Soon you will also be able to copy/paste the selected text as well as have better text editing interactions with text field focus handling, form editing and more. + +### **Scrolling** + +There are two ways scrolling can make a Flutter web app feel more natural: scrolling performance and scrolling affordance. + +Whether a mobile or web app, scrolling performance can dictate whether an app is considered “janky” or not. For our rendering system, scrolling is defining a particular usage pattern on how and when we load content. We’ve specifically focused on improving the performance of scrolling this past quarter by [optimizing static content scrolling](https://github.com/flutter/engine/pull/17621) and adding [performance benchmarks](https://docs.google.com/spreadsheets/d/10AXb-4ulJipM6ZtiieFEBXhmybB6-_gTV6HDQlgcQdA/edit#gid=2047450737) to ensure we don’t regress in the future. + +Since Flutter’s framework was initially designed for native mobile apps, Flutter web apps already have excellent support for gestures and scrolling physics for mobile browsers. Scrolling behaviors for desktop browsers are contingent on the work we do for Flutter desktop on a framework level. So far, we are able to process drag scrolling with a mouse and other mouse/wheel events, but we still need to add keyboard scrolling support. + +### **Navigation** + +Unlike native apps, web apps come with a built-in back button in the browser. Today the browser’s back button behaves like `Navigator.pop()` in any Flutter app. This means that when a user opens a link to a Flutter web app in the browser, the framework splits the initial route into parts and pushes each one. + +As an example of Flutter’s navigation support today, you can try going from [google.com](https://google.com) to [gallery.flutter.dev/#/demo/banner](https://gallery.flutter.dev/#/demo/banner), and then clicking the browser back button. + +<DashImage figure src="images/1cDlTCoUW0SRMVUl4B1sDPw.webp" alt="Today the browser back button tries to match the Flutter app’s back button routing history." caption="Today the browser back button tries to match the Flutter app’s back button routing history." /> + + +You’ll notice it doesn’t take you back to google.com, as you might expect, but instead the home page of the site: gallery.flutter.dev. Flutter splits the initial gallery app path, pushing both gallery.flutter.dev and the /demo/banner page, so when the browser back button triggers `Navigator.pop()` it goes back to gallery.flutter.dev because that’s how the app’s internal history was set up. + +There’s an ongoing effort to improve navigation/routing in Flutter for all platforms with [Navigator 2.0](http://flutter.dev/go/navigator-with-router) and a new [Router widget](http://flutter.dev/go/router-and-widgetsapp-integration). This will not only make navigation more declarative and flexible, but also improve how routing history works for Flutter web apps. + +## Being accessible + +By design, Flutter implements accessibility by building a `SemanticsNode` tree separate from the `RenderObject` tree. Flutter’s accessibility system is currently opt-in, meaning an end-user has to choose whether to enable assistive technology. When enabled, the `SemanticsNode` tree is produced by scanning the render tree and merging semantic nodes that represent a single logical interactive element on the screen. + +To do this on the web, we generate a second DOM tree parallel to the DOM tree used as the `RenderObject` tree and translate the flags, actions, labels, and other semantic properties into ARIA. + +Every Flutter web app can be made [accessible](https://flutter.dev/docs/development/accessibility-and-localization/accessibility). For example, the [Flutter Gallery app](https://gallery.flutter.dev/#/) is accessible once you have first enabled a screen reader assistive technology like [VoiceOver](https://www.apple.com/accessibility/mac/vision/), which works for web apps on both iOS and macOS. When you follow the screen reader instructions to explicitly enable accessibility for the app, you’ll see a semantic tree is produced. + +<DashImage figure src="images/0Qvklox5veq6mDZhF.webp" alt="Every Flutter web app exposes a semantic tree to accessibility tools like VoiceOver." caption="Every Flutter web app exposes a semantic tree to accessibility tools like VoiceOver." /> + + +Flutter’s web semantic features have reached parity with what exists for Flutter’s semantic system today, but we need to add automated testing to ensure that we don’t have regressions. There are complex features that we still need to support such as [tables](https://github.com/flutter/flutter/issues/45205) and [relationships between elements](https://developers.google.com/web/fundamentals/accessibility/semantics-aria/aria-labels-and-relationships). + +## Rive: A showcase example of Flutter on the web + +A prime example of a rich interactive application that demonstrates the capabilities of Flutter’s web support is Rive, who are today launching a rewrite of their design collaboration tool that is built entirely with Flutter. + +Rive is an animation design application that allows designers and developers to create high-quality assets to be easily integrated to any platform. In order to combine user interface with glitch-free animations in real-time, Rive requires heavy rendering of graphics and performant experiences across modern browsers as well as other platforms. + +[Rive 2 beta](https://blog.rive.app/announcing-rive-2/) is completely rewritten in Flutter, and as a result, offers both web and desktop versions from the same codebase. Their web experience uses the CanvasKit backend to deliver a consistent experience with Skia across both platforms. Stay tuned for upcoming technical blog posts from Rive describing how they rebuilt their tool using Flutter. For now, you can [sign up](https://beta.rive.app) for one of their limited slots to try out their beta web app. + +<YoutubeEmbed id="rJZFaeCVuqE" title="" fullwidth="true"/> + + +## Conclusion + +We hope this gives you a deeper understanding of how we are making full use of the power of the web platform to build out Flutter support for the web. Just a few years ago, it would not have been possible for us to deliver Flutter on the web with an acceptable level of quality and performance, but the introduction of new web technologies and continued advances in the platform have let us harness much more of the potential of the underlying device. As the web continues to evolve, we will evolve with it and improve our support for text interactions, scrolling, navigation, and accessibility as we continue to work towards to a stable release. + +Check out our documentation on [getting started with building Flutter web apps](https://flutter.dev/docs/get-started/web). If you’ve already built something, we’d love to see it! Share what you’re working on, either as a [CodePen](https://codepen.io/pen/editor/flutter) or with a URL. You can find us at [@flutterdev](https://twitter.com/FlutterDev) on Twitter — we can’t wait to see what you create! \ No newline at end of file diff --git a/sites/www/content/blog/google-hosting-flutter-live-on-dec-4-for-updates-on-new-way-to-build-mobile-apps/images/1WCwp_dTUQuDPDQIa1PV3cA.jpeg b/sites/www/content/blog/google-hosting-flutter-live-on-dec-4-for-updates-on-new-way-to-build-mobile-apps/images/1WCwp_dTUQuDPDQIa1PV3cA.jpeg new file mode 100644 index 0000000000..e53d794cbf Binary files /dev/null and b/sites/www/content/blog/google-hosting-flutter-live-on-dec-4-for-updates-on-new-way-to-build-mobile-apps/images/1WCwp_dTUQuDPDQIa1PV3cA.jpeg differ diff --git a/sites/www/content/blog/google-hosting-flutter-live-on-dec-4-for-updates-on-new-way-to-build-mobile-apps/images/1zc_G0l9afXZxqPAZ7rmrGQ.webp b/sites/www/content/blog/google-hosting-flutter-live-on-dec-4-for-updates-on-new-way-to-build-mobile-apps/images/1zc_G0l9afXZxqPAZ7rmrGQ.webp new file mode 100644 index 0000000000..0d3af8372a Binary files /dev/null and b/sites/www/content/blog/google-hosting-flutter-live-on-dec-4-for-updates-on-new-way-to-build-mobile-apps/images/1zc_G0l9afXZxqPAZ7rmrGQ.webp differ diff --git a/sites/www/content/blog/google-hosting-flutter-live-on-dec-4-for-updates-on-new-way-to-build-mobile-apps/index.md b/sites/www/content/blog/google-hosting-flutter-live-on-dec-4-for-updates-on-new-way-to-build-mobile-apps/index.md new file mode 100644 index 0000000000..63c0f346a7 --- /dev/null +++ b/sites/www/content/blog/google-hosting-flutter-live-on-dec-4-for-updates-on-new-way-to-build-mobile-apps/index.md @@ -0,0 +1,51 @@ +--- +title: "Google hosting Flutter Live on Dec 4 for updates on new way to build mobile apps" +description: "Tl;dr: Flutter Live, a celebration of Google’s new mobile app SDK, is happening on December 4th. Sign up here for our global livestream…" +publishDate: 2018-11-15 +author: aguinis +image: images/1WCwp_dTUQuDPDQIa1PV3cA.jpeg +category: events +layout: blog +--- + +### Tl;dr: [Flutter Live](https://developers.google.com/events/flutter-live/), a celebration of Google’s new mobile app SDK, is happening on December 4th. [Sign up here](https://docs.google.com/forms/d/e/1FAIpQLSc95VLm0odas0ieZ7-6K-5B95piV-SfgVvVxVwV39tZl_iSHw/viewform) for our global livestream and watch it at [g.co/FlutterLive](http://g.co/FlutterLive). + +## What to expect at Flutter Live + +[Flutter Live](https://developers.google.com/events/flutter-live/) is happening on December 4th, with a small in-person event at the Science Museum in London, and a large global livestream audience. This event is a celebration of [Flutter](http://flutter.io), Google’s free and open source SDK for building high-quality native iOS and Android apps from a single codebase. + +<YoutubeEmbed id="hFOS9VGs6yo" title="FL18300 v3" fullwidth="true"/> + + +## Tune in to the livestream + +Go to [g.co/FlutterLive](http://g.co/FlutterLive) and [sign up for livestream updates](https://docs.google.com/forms/d/e/1FAIpQLSc95VLm0odas0ieZ7-6K-5B95piV-SfgVvVxVwV39tZl_iSHw/viewform). The event will be broadcasted on the website Dec 4 with a pre-show starting at [4:00 p.m. GMT](https://www.timeanddate.com/worldclock/converter.html?iso=20181204T160000&p1=136&p2=137&p3=179&p4=213&p5=37&p6=771&p7=33) and the keynote at [5:00 p.m. GMT](https://www.timeanddate.com/worldclock/converter.html?iso=20181204T170000&p1=136&p2=137&p3=179&p4=213&p5=37&p6=771&p7=33). + +You can also add this directly on your Google Calendar by [clicking here](https://goo.gl/sDmu12). + +## Join a local viewing party + +Organizations all over the world are hosting free viewing parties to watch and discuss Flutter Live. Find one of the 150+ parties happening near you; [here is the full list](https://developers.google.com/events/flutter-live/viewing-parties/). + +<DashImage figure src="images/1zc_G0l9afXZxqPAZ7rmrGQ.webp" /> + + +## Get Involved with #MyFlutterStory, #AskFlutter, and #FlutterLive + +Presented from the Science Museum in London, we are optimizing Flutter Live to reach the largest audience of mobile developers and provide all the rigor and excitement of an in-person experience to our global online audience. ***Regardless of where you watch the event, there are three ways to get involved.*** + +### #MyFlutterStory + +Flutter is open source and community driven. We value our community and want to share their story with the rest of the world. That’s why we are giving you the opportunity to share your #MyFlutterStory. [Here are the guidelines](https://docs.google.com/document/d/1Lt0SaCvNF32rLVQf0yLC8cl9HElPYhEXeVXXEqg1Eso/edit) to submitting your story — it might show up at the event! We also encourage you to [tweet out your video](https://twitter.com/search?q=myflutterstory&src=typd) with the #MyFlutterStory hashtag. + +### #AskFlutter + +Andrew Brogdon from our team will be LIVE on-site answering questions in real time that are posted with #AskFlutter. Tweet your questions and comments with that hashtag and your tweet may appear on the global livestream right after the keynote. + +### #FlutterLive + +This is our overall event hashtag. We will have a social wall that is constantly showing tweets coming in with #FlutterLive both on-site and on our livestream. Make sure to tweet with pictures, comments, videos, and questions while you experience Flutter Live. + +We are thrilled that you will experience Flutter Live with us on December 4th. In the meantime, follow us on twitter at [@flutterio](https://twitter.com/flutterio) and get started with Flutter at [flutter.io](http://flutter.io). + +<DashImage figure src="images/1WCwp_dTUQuDPDQIa1PV3cA.jpeg" /> diff --git a/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/03xKb80lXnsfJg3pP.webp b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/03xKb80lXnsfJg3pP.webp new file mode 100644 index 0000000000..3ba60cd804 Binary files /dev/null and b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/03xKb80lXnsfJg3pP.webp differ diff --git a/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/084RzNeoAVCQu3shZ.webp b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/084RzNeoAVCQu3shZ.webp new file mode 100644 index 0000000000..3ba60cd804 Binary files /dev/null and b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/084RzNeoAVCQu3shZ.webp differ diff --git a/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0KRHUVfNaxppJLr86.webp b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0KRHUVfNaxppJLr86.webp new file mode 100644 index 0000000000..526c607921 Binary files /dev/null and b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0KRHUVfNaxppJLr86.webp differ diff --git a/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0SHp5ZrnQvQ_bva8K.webp b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0SHp5ZrnQvQ_bva8K.webp new file mode 100644 index 0000000000..4cb9040236 Binary files /dev/null and b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0SHp5ZrnQvQ_bva8K.webp differ diff --git a/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0V8DV37bsn0UNXkvE.webp b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0V8DV37bsn0UNXkvE.webp new file mode 100644 index 0000000000..a2f79c7153 Binary files /dev/null and b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0V8DV37bsn0UNXkvE.webp differ diff --git a/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0WeR31rSBMqkRT6q_.webp b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0WeR31rSBMqkRT6q_.webp new file mode 100644 index 0000000000..664a523210 Binary files /dev/null and b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0WeR31rSBMqkRT6q_.webp differ diff --git a/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0eJK4geRY6r6kvPb-.webp b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0eJK4geRY6r6kvPb-.webp new file mode 100644 index 0000000000..a2ae31634b Binary files /dev/null and b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0eJK4geRY6r6kvPb-.webp differ diff --git a/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0h1swTfindfOd_uAB.webp b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0h1swTfindfOd_uAB.webp new file mode 100644 index 0000000000..3ba60cd804 Binary files /dev/null and b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0h1swTfindfOd_uAB.webp differ diff --git a/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0jfsCt8xhuizJO7uf.webp b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0jfsCt8xhuizJO7uf.webp new file mode 100644 index 0000000000..c3b393a7d2 Binary files /dev/null and b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0jfsCt8xhuizJO7uf.webp differ diff --git a/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0s0fEzRKPUlPITzxE.webp b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0s0fEzRKPUlPITzxE.webp new file mode 100644 index 0000000000..e2e7deb891 Binary files /dev/null and b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/images/0s0fEzRKPUlPITzxE.webp differ diff --git a/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/index.md b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/index.md new file mode 100644 index 0000000000..55b298921c --- /dev/null +++ b/sites/www/content/blog/google-i-o-spotlight-flutter-in-action-at-bytedance/index.md @@ -0,0 +1,121 @@ +--- +title: "Google I/O spotlight: Flutter in action at ByteDance" +description: "Note: This article, originally written in Chinese by the ByteDance team, has been translated to English." +publishDate: 2021-06-29 +author: sfshaza2 +image: images/0s0fEzRKPUlPITzxE.webp +category: events +layout: blog +--- + +Flutter, a technology that [ByteDance](https://www.bytedance.com/en/) has been using and contributing to for some years now, was recently highlighted on the main stage of Google I/O. Developed and open sourced by Google, the multi-platform framework for front end UI development, has garnered over 120,000 stars on GitHub. + +<DashImage figure src="images/0s0fEzRKPUlPITzxE.webp" alt="At Google I/O, Zoey Fan, one of Flutter’s product managers, talked about how the framework was adopted at ByteDance." caption="At Google I/O, Zoey Fan, one of Flutter’s product managers, talked about how the framework was adopted at ByteDance." /> + + +<DashImage figure src="images/0V8DV37bsn0UNXkvE.webp" alt="More than 70 apps by ByteDance use Flutter as their multi-platform solution." caption="More than 70 apps by ByteDance use Flutter as their multi-platform solution." /> + + +<DashImage figure src="images/0jfsCt8xhuizJO7uf.webp" alt="Flutter has reduced development time by 33%, as compared to developing separate apps for Android and iOS." caption="Flutter has reduced development time by 33%, as compared to developing separate apps for Android and iOS." /> + + +Today, there are more than 500 Flutter developers at ByteDance, and more than 200 of those are actively developing with Flutter. The developers use Flutter not only for mobile apps, but are also experimenting with it on web, desktop, and embedded platforms. + +Beyond this, ByteDance fundamental work across the organization and has made major contributions to the Flutter project by submitting dozens of pull requests (PRs). + +<DashImage figure src="images/084RzNeoAVCQu3shZ.webp" /> + + +## What has ByteDance done to make Flutter really work for them? + +The story of Flutter at ByteDance started 2 years ago. + +At the time, the ByteDance front end engineering team noticed that many teams within the company needed to develop for multiple platforms, but they lacked a tool to achieve high-efficiency, high-performance, multi-platform development. + +When Google open sourced Flutter, the ByteDance team discovered that with Flutter they only needed to develop the app once to support platforms such as Android, iOS, and web. Also, because Flutter has its own rendering engine, they could achieve more consistent performance across platforms. + +With Flutter the Android, iOS, and web versions of an app automatically stay in sync. There is no need to design and program the UI separately for each platform, so a significant portion of redundant work is eliminated. + +To support business development more efficiently, the ByteDance team performed fundamental work on the framework itself, such as optimizing performance, creating app frameworks, containerizing, and supporting add to app. They also improved Flutter performance tools including improvements to the Frames Per Second (FPS) info in the [Frame chart](https://flutter.dev/docs/development/tools/devtools/performance#flutter-frames-chart), and to the [timeline events chart](https://flutter.dev/docs/development/tools/devtools/performance#timeline-events-chart). Both of these charts are part of the [Performance View](https://flutter.dev/docs/development/tools/devtools/performance) in [Flutter DevTools](https://flutter.dev/docs/development/tools/devtools/overview). + +When adopting Flutter, the ByteDance team encountered some unique challenges. For example, Flutter must be added to the app installation package increasing the size of the app downloaded by the users. Additionally, Flutter uses the Dart programming language, which is larger in size than native code, further increasing the package size. + +The ByteDance team started a special plan to optimize the package size by compressing the iOS data section, and stripping out the Skia library and other libraries (such as BoringSSL, ICU, text rendering, and libwebp). They analyzed Flutter Dart code against iOS native code and found that, to implement the same business feature, the Dart code generated more machine code instructions. To close the gap, they reduced alignment of instructions, removed debugging trap instructions, dropped redundant initialization of stores with null, removed `RawInstruction` headers with bare instructions mode, compressed `StackMap`s , removed `CodeSourceMap`, and so on. + +<DashImage figure src="images/0SHp5ZrnQvQ_bva8K.webp" /> + + +Individually, each of these optimizations reduced the package size by 0.2 to 4 MB, and significantly reduced the total package size when combined. The ByteDance team shared their experience with Google engineers, and many improvements made their way to the Flutter open source project for the benefit of the larger community. + +However, when ByteDance released their first Flutter app, new issues emerged. Users asked: ‘Why is the UI so janky when I scroll in the app?’ + +When the ByteDance team looked into the issue, they saw that when a `FlutterView` extended a `TextureView`, the UI was noticeably jankier than when it extended `SurfaceView`. However, in the official [Timeline](https://flutter.dev/docs/development/tools/devtools/performance#timeline-events-chart) tool, the UI thread time and GPU thread time for each rendered frame are about the same, with `TextureView` pulling a bit ahead occasionally. + +The metrics contradicted the real-world user experience, which puzzled the team. + +At first, the team used the [Timeline](https://flutter.dev/docs/development/tools/devtools/performance#timeline-events-chart) tool to troubleshoot the issue, to no avail. After digging into the tool’s source code, they discovered the root cause of the issue. + +`SurfaceView` had better performance than `TextureView`. Because `SurfaceView` had its own surface, and rendering was performed in its own OpenGL context, it could interact with `SurfaceFlinger` independently, and took full advantage of triple-buffering. On the other hand, `TextureView` was a regular view that depended on the surface of its host window for rendering. That meant the rendering wasn’t performed immediately after the UI and GPU threads had finished their work, but needed to wait for the native main thread and `renderThread` before the view could interact with `SurfaceFlinger`. That was a much longer rendering pipeline than that of `SurfaceView`. + +These findings not only helped the team eliminate the jank, but resulted in 10 PRs being submitted to the Flutter open source project. With this fundamental work done, Flutter eventually became the go-to framework for multi-platform app development at ByteDance. Soon, the ByteDance team’s work with Flutter will be available to external developers using their mobile development framework, [veMARS](https://www.volcengine.com/product/vemars), benefiting the entire developer community. + +<DashImage figure src="images/03xKb80lXnsfJg3pP.webp" /> + + +## From experiment to production, this is how ByteDance put Flutter into use + +It wasn’t exactly smooth sailing for ByteDance to put Flutter into real-world use. + +At first, the ByteDance team chose a mature product and planned to re-implement the app’s video playback feature with Flutter. + +The feature, originally written in native code for Android and iOS, wasn’t straightforward to rewrite with Flutter. After 6 months, the team came to the conclusion that it would be difficult to make all the live data compatible, and challenging to update the existing business logic. + +The team decided that it wasn’t productive to update the existing features of a mature product with the new framework. Flutter’s strengths would be better used in a brand new app. The team lead said, “In a mature product, everything is already well built with native Android or iOS technology. There isn’t much gain in re-implementing the features with Flutter only to make minor improvements. It also increases the package size since the Flutter engine is included in the package. In new products or new scenarios, however, Flutter can greatly increase our productivity.” + +With this changed mindset, the team turned their focus to new business areas such as education. + +One of their education apps in China helps students learn the order of strokes of Chinese characters; the team wanted to add a stroke tracking feature. + +To implement it, the team took inspiration from some open source projects, and decided to use SVG paths to represent strokes. The paths would then be adjusted and positioned to compose the characters: + +<DashImage figure src="images/0eJK4geRY6r6kvPb-.webp" /> + + +They defined the skeleton of each stroke to guide the movement of the virtual brush pen, so the pen moves just like it would in calligraphy: + +<DashImage figure src="images/0KRHUVfNaxppJLr86.webp" /> + + +Based on the defined order of the skeletons, a circle with a certain radius is drawn along each skeleton, and together these circles form the stroke. After that, the team added key frames to ensure that the frame rate of the animation is high enough to avoid jank. + +That is how they created the smooth tracking effect, as shown in the following GIF: + +<DashImage figure src="images/0WeR31rSBMqkRT6q_.webp" /> + + +The feature, built with Flutter, now supports more than 9,000 Chinese characters, including most of the commonly used characters. Compared to developing with native code, Flutter saved time and resources. + +Today, many apps by ByteDance employ a hybrid approach to development, combining the strengths of Flutter and other technologies, with newer apps leaning toward pure Flutter. For apps such as Xigua Video, TikTok Volcano, and Open Language, Flutter increased the productivity of the teams by about 33%. + +<DashImage figure src="images/0h1swTfindfOd_uAB.webp" /> + + +## ByteDancers embrace the latest technology + +Even now, the Flutter team at ByteDance continues to explore the latest technologies. According to the team lead, “We have in our team many tech enthusiasts with global vision, and will continue to explore global technology developments and discuss the implementation of technology. We have close connections and collaborations with many tech companies. We have quarterly sync meetings with Google, for instance, to exchange progress, thoughts, needs, and ideas from both sides.” + +One day, the maintainer of the Dart open source project on GitHub came to the ByteDance team lead with the following remarks, “Someone from your team submitted more than a dozen PRs to Dart and they’re all very good and well thought out.“ + +The Dart open source project maintainer was talking about Frank. Frank is a passionate open source contributor, and just got his bachelor’s degree 3 years ago. His journey in the open source world first started during his first year of university in 2015. One of the projects he created and open sourced on GitHub had over 700 stars. “It has had hundreds of downloads per year, and many game developers use it to create demos“, Frank said. + +After graduation, Frank joined the Flutter team at ByteDance and became one of the most active open source contributors on the team, contributing a number of PRs to Dart and Flutter. Frank remembers that when he was working on the package size issues he proactively followed up on a relevant issue on the Dart GitHub project, and he noticed that the `Specializer` component could use some further tuning. He created a patch with his improvements to the Dart compiler middleware and submitted it to the project. The patch wasn’t accepted initially because of the large number of code blocks affected and a few minor concerns. He modified the patch seven times before it was accepted, and it was merged into the code base a week later. + +There are many other open source enthusiasts like Frank in the Flutter team at ByteDance. + +The ByteDance team summarized this passionate attitude toward innovation with the following words: + +“There are indeed many people in the industry who prefer mature technology, but it takes time for every technology to mature, and there will always be people like us who love to stay on the cutting edge.” + +This is especially true for something as novel as Flutter. There needs to be some daring people who take the first steps. At ByteDance, the Flutter engineering team, as well as the engineering teams that they support, actively try and embrace new technologies. Doing this benefited ByteDance tremendously and greatly increased our productivity. + +ByteDance has always wanted to be part of things that could push the industry forward, and Flutter is likely to be one of those things. \ No newline at end of file diff --git a/sites/www/content/blog/google-maps-and-flutter/images/12L9hKzQdpz2YfSfkKQ5fYg.webp b/sites/www/content/blog/google-maps-and-flutter/images/12L9hKzQdpz2YfSfkKQ5fYg.webp new file mode 100644 index 0000000000..99f5bf86da Binary files /dev/null and b/sites/www/content/blog/google-maps-and-flutter/images/12L9hKzQdpz2YfSfkKQ5fYg.webp differ diff --git a/sites/www/content/blog/google-maps-and-flutter/images/14dSyF9z9lAYvHVxFPS_oiw.webp b/sites/www/content/blog/google-maps-and-flutter/images/14dSyF9z9lAYvHVxFPS_oiw.webp new file mode 100644 index 0000000000..80594b88d9 Binary files /dev/null and b/sites/www/content/blog/google-maps-and-flutter/images/14dSyF9z9lAYvHVxFPS_oiw.webp differ diff --git a/sites/www/content/blog/google-maps-and-flutter/images/1QTwIbpR2MgBuEgwt9zZK-w.webp b/sites/www/content/blog/google-maps-and-flutter/images/1QTwIbpR2MgBuEgwt9zZK-w.webp new file mode 100644 index 0000000000..1b472b66b9 Binary files /dev/null and b/sites/www/content/blog/google-maps-and-flutter/images/1QTwIbpR2MgBuEgwt9zZK-w.webp differ diff --git a/sites/www/content/blog/google-maps-and-flutter/images/1Z72fYzGsnj_UVNQZx9o-2A.webp b/sites/www/content/blog/google-maps-and-flutter/images/1Z72fYzGsnj_UVNQZx9o-2A.webp new file mode 100644 index 0000000000..48f3be9223 Binary files /dev/null and b/sites/www/content/blog/google-maps-and-flutter/images/1Z72fYzGsnj_UVNQZx9o-2A.webp differ diff --git a/sites/www/content/blog/google-maps-and-flutter/images/1b_SmpOTvtt2G6HWwQpQFdQ.webp b/sites/www/content/blog/google-maps-and-flutter/images/1b_SmpOTvtt2G6HWwQpQFdQ.webp new file mode 100644 index 0000000000..687be5ade5 Binary files /dev/null and b/sites/www/content/blog/google-maps-and-flutter/images/1b_SmpOTvtt2G6HWwQpQFdQ.webp differ diff --git a/sites/www/content/blog/google-maps-and-flutter/images/1fEcIp4V3mkrATwJl7qhSJg.webp b/sites/www/content/blog/google-maps-and-flutter/images/1fEcIp4V3mkrATwJl7qhSJg.webp new file mode 100644 index 0000000000..753f3817ce Binary files /dev/null and b/sites/www/content/blog/google-maps-and-flutter/images/1fEcIp4V3mkrATwJl7qhSJg.webp differ diff --git a/sites/www/content/blog/google-maps-and-flutter/index.md b/sites/www/content/blog/google-maps-and-flutter/index.md new file mode 100644 index 0000000000..67e42f96d4 --- /dev/null +++ b/sites/www/content/blog/google-maps-and-flutter/index.md @@ -0,0 +1,430 @@ +--- +title: "Google Maps and Flutter" +description: "This article will show you step-by-step how to add a Google Map widget to your Flutter application." +publishDate: 2018-12-14 +author: kenzieschmoll +image: images/14dSyF9z9lAYvHVxFPS_oiw.webp +category: spotlight +layout: blog +--- + +## Adding Google Maps to Flutter + +*This article was last updated on 03/19/19. The API for this plugin changed to be more idiomatic to Flutter. The article now reflects those API changes. Note that this plugin is still in Developers Preview*. + +This article shows you step-by-step how to add a Google Map widget to your Flutter application. Here’s what you’re going to build today: + +<DashImage figure src="images/14dSyF9z9lAYvHVxFPS_oiw.webp" /> + + +## Setup + +The first step is to add the Google Maps Flutter plugin as a dependency in the **pubspec.yaml** file. The package is available as [`google_maps_flutter`](https://pub.dartlang.org/packages/google_maps_flutter) on [pub.dartlang.org](https://pub.dartlang.org/). + +```yaml +dependencies: + ... + google_maps_flutter: ^0.4.0 +``` + +Once you do that, you need to run **flutter packages get**. + +The next step is getting an API key for both Android and iOS. For Android, follow the instructions at [Maps SDK for Android — Get API Key](https://developers.google.com/maps/documentation/android-sdk/signup). Once you have your API key, add it to your Flutter app in the application manifest ([`android/app/src/main/AndroidManifest.xml`](https://gist.github.com/kenzieschmoll/21ee5723ce83fb0675dcde202fbd6dca)), as follows: + +```xml +<manifest ... + <application ... + <meta-data android:name="com.google.android.geo.API_KEY" + android:value="YOUR ANDROID API KEY HERE"/> +``` + +For iOS, follow the instructions at [Maps SDK for iOS — Get API Key](https://developers.google.com/maps/documentation/ios-sdk/get-api-key). Once you have this key, add it to your Flutter app in the application delegate ([`ios/Runner/AppDelegate.m`](https://gist.github.com/kenzieschmoll/15aee1e8e3fbab3ba25a6a3c9425bbfb)): + +```objective-c +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" +// Add the GoogleMaps import. +#import "GoogleMaps/GoogleMaps.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Add the following line with your API key. + [GMSServices provideAPIKey:@"YOUR IOS API KEY HERE"]; + [GeneratedPluginRegistrant registerWithRegistry:self]; + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} +@end +``` + +On iOS, you also need to add a setting to the app’s **Info.plist** file ([`ios/Runner/Info.plist`](https://gist.github.com/kenzieschmoll/e3c35fa3578fd17119384b26c2474fec)). This entry forces Flutter on iOS into a single threaded mode, which is required for the platform view embedding to work. This technical restriction is being worked on and will be lifted before Google Maps moves out of Developer Preview. + +Add a boolean property with the key `io.flutter.embedded_views_preview` and the value `true`: + +```openstep property list +<key>io.flutter.embedded_views_preview</key> +<true/> + +``` + +## Adding a GoogleMap widget + +Now you are ready to add a GoogleMap widget! Run **flutter clean** to make sure the API key changes are picked up on the next build. Then, add a GoogleMap widget that covers the entire screen: + +```dart +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; + +void main() => runApp(MyApp()); + +class MyApp extends StatefulWidget { + @override + _MyAppState createState() => _MyAppState(); +} + +class _MyAppState extends State<MyApp> { + Completer<GoogleMapController> _controller = Completer(); + + static const LatLng _center = const LatLng(45.521563, -122.677433); + + void _onMapCreated(GoogleMapController controller) { + _controller.complete(controller); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: Text('Maps Sample App'), + backgroundColor: Colors.green[700], + ), + body: GoogleMap( + onMapCreated: _onMapCreated, + initialCameraPosition: CameraPosition( + target: _center, + zoom: 11.0, + ), + ), + ), + ); + } +} +``` + +Important bits: + +* **onMapCreated**: method that is called on map creation and takes a MapController as a parameter. + +* **initialCameraPosition**: required parameter that sets the starting camera position. Camera position describes which part of the world you want the map to point at. + +* **mapController**: manages camera function (position, animation, zoom). This pattern is similar to other controllers available in Flutter, for example [TextEditingController](https://docs.flutter.io/flutter/widgets/TextEditingController-class.html). + +If you run your app at this point, it should look like this: + +<DashImage figure src="images/1fEcIp4V3mkrATwJl7qhSJg.webp" /> + + +## Upgrading to Google Maps SDK for iOS 3.0 + +After running your app in the last step, you may have seen the warning: “*New version of Google Maps SDK for iOS available: 3.0*”. This is because Flutter supports a minimum of iOS version 8, while the latest version of Google Maps SDK for iOS supports minimum iOS version 9. In order to use the latest version of Google Maps SDK for iOS, we need to raise our iOS for Flutter minimum version to 9. + +Declare a minimum platform version of 9.0 by uncommenting this line in **ios/Podfile**: `platform :ios, ‘9.0’`. + +Then run **(cd ios && pod update)** from command line to pull the newest version of the Google Maps SDK for iOS. + +Next, return to your main project directory (**cd ..)** and run **open ios/Runner.xcworkspace/** from command line. This will open the iOS Runner Xcode project, where we need to set the Deployment Target iOS version to 9.0. + +<DashImage figure src="images/1QTwIbpR2MgBuEgwt9zZK-w.webp" /> + + +Run your app again, and verify the new version available warning is no longer there. + +## What can you do with a Google Map? + +So now you have Google Maps in your app, but you probably want to do something more interesting. What about putting Flutter widgets on top of the map, changing the map’s appearance, or adding place markers to the map? You can do it all! + +### Add a widget on top of the map + +It’s important to remember that the GoogleMap widget is just a Flutter widget, meaning you can treat it like any other widget. This includes placing another widget on top of it. By placing the GoogleMap widget inside of a Stack widget, you can layer other Flutter widgets on top of the map widget: + +```dart +body: Stack( + children: <Widget>[ + GoogleMap( + onMapCreated: _onMapCreated, + initialCameraPosition: CameraPosition( + target: _center, + zoom: 11.0, + ), + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: Align( + alignment: Alignment.topRight, + child: FloatingActionButton( + onPressed: () => print('button pressed'), + materialTapTargetSize: MaterialTapTargetSize.padded, + backgroundColor: Colors.green, + child: const Icon(Icons.map, size: 36.0), + ), + ), + ), + ], +), +``` + +### Change the map’s appearance + +Right now, the added button doesn’t do anything interesting. Change that so that when pressed, the button toggles between two different map types: normal view and satellite view. + +To do this, create a variable `_currentMapType` to keep track of the current map type. Add `mapType: _currentMapType` to the GoogleMap widget. + +```dart +MapType _currentMapType = MapType.normal; + +@override +Widget build(BuildContext context) { + return MaterialApp( + ... + GoogleMap( + ... + mapType: _currentMapType, + ), + ); +} +``` + +Now, add a method that will modify the value of `_currentMapType` inside of a `setState()` call. This will update the appearance of the map to match the new value of `_currentMapType`. + +```dart +void _onMapTypeButtonPressed() { + setState(() { + _currentMapType = _currentMapType == MapType.normal + ? MapType.satellite + : MapType.normal; + }); +} +``` + +Finally, replace `() => print(‘button pressed’)` with `_onMapTypeButtonPressed`. + +``` +child: FloatingActionButton( + onPressed: **_onMapTypeButtonPressed, + ...** +), +``` + + +<DashImage figure src="images/12L9hKzQdpz2YfSfkKQ5fYg.webp" /> + + +### Add a marker + +How about creating another button that, when pressed, adds place markers to the map. Following the same pattern as before, add a button to the stack. Place another FloatingActionButton inside the Align widget from the build method. You’ll need to wrap both FloatingActionButtons in a Column widget: + +```dart +Align( + alignment: Alignment.topRight, + child: Column( + children: <Widget>[ + FloatingActionButton( + ... + ), + SizedBox(height: 16.0), + FloatingActionButton( + onPressed: _onAddMarkerButtonPressed, + materialTapTargetSize: MaterialTapTargetSize.padded, + backgroundColor: Colors.green, + child: const Icon(Icons.add_location, size: 36.0), + ), + ], + ), +), + +``` + +To implement the `_onAddMarkerButtonPressed` method, we need to do a couple things. First, create a variable called `_markers` to store the map’s markers. Set this as the `markers` property of the GoogleMap widget. + +```dart +final Set<Marker> _markers = {}; + +@override +Widget build(BuildContext context) { + return MaterialApp( + ... + GoogleMap( + ... + markers: _markers, + ), + ); +} +``` + +To add a marker in the center of the map, we need to track the map’s current camera position. Add the following code to do that: + +```dart +LatLng _lastMapPosition = _center; + +void _onCameraMove(CameraPosition position) { + _lastMapPosition = position.target; +} + +@override +Widget build(BuildContext context) { + return MaterialApp( + ... + GoogleMap( + ... + onCameraMove: _onCameraMove, + ), + ); +} +``` + +Now, we can add a marker to the map by modifying the content of `_markers` inside of a `setState()` call. + +```dart +void _onAddMarkerButtonPressed() { + setState(() { + _markers.add(Marker( + // This marker id can be anything that uniquely identifies each marker. + markerId: MarkerId(_lastMapPosition.toString()), + position: _lastMapPosition, + infoWindow: InfoWindow( + title: 'Really cool place', + snippet: '5 Star Rating', + ), + icon: BitmapDescriptor.defaultMarker, + )); + }); +} +``` + +<DashImage figure src="images/1b_SmpOTvtt2G6HWwQpQFdQ.webp" /> + + +You can customize the markers with different colors, for example `BitmapDescriptor.*defaultMarkerWithHue*(BitmapDescriptor.*hueViolet*),` or even with custom icons, for example `BitmapDescriptor.fromAsset(‘assets/asset_name.png)`. + +### Final main.dart code + +```dart +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; + +void main() => runApp(MyApp()); + +class MyApp extends StatefulWidget { + @override + _MyAppState createState() => _MyAppState(); +} + +class _MyAppState extends State<MyApp> { + Completer<GoogleMapController> _controller = Completer(); + + static const LatLng _center = const LatLng(45.521563, -122.677433); + + final Set<Marker> _markers = {}; + + LatLng _lastMapPosition = _center; + + MapType _currentMapType = MapType.normal; + + void _onMapTypeButtonPressed() { + setState(() { + _currentMapType = _currentMapType == MapType.normal + ? MapType.satellite + : MapType.normal; + }); + } + + void _onAddMarkerButtonPressed() { + setState(() { + _markers.add(Marker( + // This marker id can be anything that uniquely identifies each marker. + markerId: MarkerId(_lastMapPosition.toString()), + position: _lastMapPosition, + infoWindow: InfoWindow( + title: 'Really cool place', + snippet: '5 Star Rating', + ), + icon: BitmapDescriptor.defaultMarker, + )); + }); + } + + void _onCameraMove(CameraPosition position) { + _lastMapPosition = position.target; + } + + void _onMapCreated(GoogleMapController controller) { + _controller.complete(controller); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: Text('Maps Sample App'), + backgroundColor: Colors.green[700], + ), + body: Stack( + children: <Widget>[ + GoogleMap( + onMapCreated: _onMapCreated, + initialCameraPosition: CameraPosition( + target: _center, + zoom: 11.0, + ), + mapType: _currentMapType, + markers: _markers, + onCameraMove: _onCameraMove, + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: Align( + alignment: Alignment.topRight, + child: Column( + children: <Widget> [ + FloatingActionButton( + onPressed: _onMapTypeButtonPressed, + materialTapTargetSize: MaterialTapTargetSize.padded, + backgroundColor: Colors.green, + child: const Icon(Icons.map, size: 36.0), + ), + SizedBox(height: 16.0), + FloatingActionButton( + onPressed: _onAddMarkerButtonPressed, + materialTapTargetSize: MaterialTapTargetSize.padded, + backgroundColor: Colors.green, + child: const Icon(Icons.add_location, size: 36.0), + ), + ], + ), + ), + ), + ], + ), + ), + ); + } +} +``` + +## What else can you do? + +Once again, *the GoogleMap widget is just a widget***.** This means you can place widgets on top of it (like you just did), you can place it inside other widgets (like a ListView, for example), or if you’re feeling a bit wild, you could even place it in a Transform widget*. + +<DashImage figure src="images/1Z72fYzGsnj_UVNQZx9o-2A.webp" /> + + +The possibilities extend as far as you’d like to take them. Checkout the [Place Tracker](https://github.com/flutter/samples/tree/master/place_tracker) app in [flutter/samples](https://github.com/flutter/samples) for a more complete Google Maps demo. You can also checkout the [google_maps_flutter plugin example app](https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter/example) for a demonstration on using the plugin. + +*Using a transform widget to rotate the map is currently not supported on iOS. It might be in the future, but it is not the biggest priority for the plugin at this time. \ No newline at end of file diff --git a/sites/www/content/blog/gsoc-21-creating-a-desktop-sample-for-flutter/images/0FwXcFHhXjCuOEaFw.webp b/sites/www/content/blog/gsoc-21-creating-a-desktop-sample-for-flutter/images/0FwXcFHhXjCuOEaFw.webp new file mode 100644 index 0000000000..4eaea42ccb Binary files /dev/null and b/sites/www/content/blog/gsoc-21-creating-a-desktop-sample-for-flutter/images/0FwXcFHhXjCuOEaFw.webp differ diff --git a/sites/www/content/blog/gsoc-21-creating-a-desktop-sample-for-flutter/images/0Lh2I-A4Aak5UZXze.webp b/sites/www/content/blog/gsoc-21-creating-a-desktop-sample-for-flutter/images/0Lh2I-A4Aak5UZXze.webp new file mode 100644 index 0000000000..dde936e11b Binary files /dev/null and b/sites/www/content/blog/gsoc-21-creating-a-desktop-sample-for-flutter/images/0Lh2I-A4Aak5UZXze.webp differ diff --git a/sites/www/content/blog/gsoc-21-creating-a-desktop-sample-for-flutter/images/1aRiqWvnUYD-cSbGhxTnUsw.jpeg b/sites/www/content/blog/gsoc-21-creating-a-desktop-sample-for-flutter/images/1aRiqWvnUYD-cSbGhxTnUsw.jpeg new file mode 100644 index 0000000000..57f299544e Binary files /dev/null and b/sites/www/content/blog/gsoc-21-creating-a-desktop-sample-for-flutter/images/1aRiqWvnUYD-cSbGhxTnUsw.jpeg differ diff --git a/sites/www/content/blog/gsoc-21-creating-a-desktop-sample-for-flutter/index.md b/sites/www/content/blog/gsoc-21-creating-a-desktop-sample-for-flutter/index.md new file mode 100644 index 0000000000..464a36683f --- /dev/null +++ b/sites/www/content/blog/gsoc-21-creating-a-desktop-sample-for-flutter/index.md @@ -0,0 +1,80 @@ +--- +title: "GSoC ’21: Creating a desktop sample for Flutter" +description: "by Abdullah Deshmukh" +publishDate: 2021-08-22 +author: abd99 +image: images/1aRiqWvnUYD-cSbGhxTnUsw.jpeg +category: events +layout: blog +--- + +<DashImage figure src="images/1aRiqWvnUYD-cSbGhxTnUsw.jpeg" /> + + +[Google Summer of Code](https://summerofcode.withgoogle.com/) (GSoC) is an open source, summer program sponsored by Google. During this program, students work on a project under the guidance of an open source organization. + +This was the second time that the Dart team participated as a mentoring organization in GSoC. Unfortunately, due to the unavailability of mentors, there weren’t any Flutter projects on the [ideas list](https://docs.google.com/document/d/1KfaTPPt6rqvgPD4kkZcsaehXC9FijcJ20V4J3FDLsRk/edit#) this year. Because it was my second time participating in GSoC, I contacted my mentor from last year, Brett, to see if working on a project would be possible. After a discussion with him and the org admin, I found a project to work on. + +This blog shows the work I did for my project. [Check out all the projects under Dart this year](https://summerofcode.withgoogle.com/organizations/4658845562961920/#projects). All of the resulting source code is in GitHub repositories, and you can see how the work progressed by looking at individual pull requests (PRs). + +## Project details: Flutter desktop tool + +This year, at Flutter Engage, a beta snapshot of Flutter’s desktop support was included in the stable channel. This caused a need for a desktop sample for the [samples](https://github.com/flutter/samples) repo that could, later on, be released on the desktop app stores like the [macOS App Store](https://developer.apple.com/macos/distribution/), the [Microsoft Store](https://docs.microsoft.com/en-us/windows/uwp/publish/), and the [Linux Snap Store](https://snapcraft.io/docs/snapcraft-overview). After a discussion with Brett and the team, we decided to build a desktop sample that would also be a tool to help developers manage lint rules for their projects. Check out the [linting_tool on GitHub](https://github.com/flutter/samples/tree/master/experimental/linting_tool). + +<DashImage figure src="images/0Lh2I-A4Aak5UZXze.webp" alt="A screenshot of the app" caption="A screenshot of the app" /> + + +The app uses the lint rules that are hosted by the Dart team from the [dart-lang/linter](https://github.com/dart-lang/linter) repo, and the app can do the following: + +**Show a list of available lint rules for Dart.** + +You can fetch a list of lint rules from an API. The app uses the [`flutter_markdown`](https://pub.dev/packages/flutter_markdown) package to format the details and the code snippets. + +Related PR: [#856](https://github.com/flutter/samples/pull/856) + +**Save the rules to different profiles.** + +You can create different rule profiles for different types of projects. The app uses the [`hive`](https://pub.dev/packages/hive) database to maintain persistent storage. + +<DashImage figure src="images/0FwXcFHhXjCuOEaFw.webp" alt="A list of saved profiles" caption="A list of saved profiles" /> + + +Related PR: [#860](https://github.com/flutter/samples/pull/860) + +**Modify and export profiles.** + +The profiles can be modified to your liking and then exported to Flutter projects as analysis_options.yaml files. The packages [`yaml`](https://pub.dev/packages/yaml), [`json2yaml`](https://pub.dev/packages/json2yaml), and [`file_selector`](https://pub.dev/packages/file_selector) are used to accomplish this. + +Related PRs: [#874](https://github.com/flutter/samples/pull/874), [#869](https://github.com/flutter/samples/pull/869) + +**Show default profiles.** + +You can view a list of default profiles that [`effective_dart`](https://pub.dev/packages/effective_dart), [`pedantic`](https://pub.dev/packages/pedantic), [`lints`](https://pub.dev/packages/lints), and [`flutter_lints`](https://pub.dev/packages/flutter_lints) use. + +Related PR: **[**#871](https://github.com/flutter/samples/pull/871) + +To see how the project progressed during the GSoC period, see [this set of PRs](https://github.com/flutter/samples/pulls?q=author%3Aabd99+created%3A2021-06-07..2021-08-31+). + +There are a few more things that I’d like to add to the project. The two main things are the ability to load profiles from existing analysis_options.yaml files and the ability to search for a specific rule in the rules list. I also have a new video series that I’m working on that will cover how to deploy Flutter desktop apps. I’ll update this blog when it’s out and will also share it on my [Twitter account](https://twitter.com/abdullahzakir99). + +## Experience + +My experience with Google Summer of Code (GSoC) this time was even better than last year. (Check out [Learn testing with the new Flutter sample](https://medium.com/flutter/learn-testing-with-the-new-flutter-sample-gsoc20-work-product-e872c7f6492a) to read more about the project I worked on last year.) I think this year was better because the project was more technical, covered more concepts, and was related to something that I never worked on before. I also knew a lot more people on the team this time. I think GSoC will be a part of my life that I won’t ever forget. + +### Mentorship + +[Brett](https://github.com/domesticmouse) was always there to help me with all my questions and blockers. He was very understanding when I had to give more time to my other commitments like school, other gigs, and family-related things. I think the best thing was that he gave me complete freedom to explore things and tweak the project’s scope if I thought that something would be a valuable addition to the project. One cool thing I did this time was I asked him general software engineering-related questions that were not directly related to the project at the end of every weekly meeting, questions like “How does Google handle estimates?” and “What do you think about remote work?”. This helped me learn things about the software industry that aren’t related to programming. I highly recommend that you try this during your internships or GSoC-like projects. And, yes, he’s still the coolest person I have ever worked with. + +### Learning + +Learning is the best part about open source software. A lot of things that I did this time couldn’t be Googled. I read a lot of code, finding how different packages do the same things and the general patterns in which they differ. I also learned how to find a great package for a project from a huge list of available ones. Because this category was new to me, this experience helped me learn a lot in a short amount of time. + +### Challenges + +There were a few challenges that I got to learn from this time. The first one was related to the timeline. This year, the timeline of GSoC was reduced in half which made us reprioritize and skip a lot of features that might have been valuable additions to the project. The other challenges were working on a completely new category and finding good resources on how to do things in a category that isn’t too popular. + +## Final thoughts + +I had an amazing experience over the past year working with Flutter and the samples repo. And, I would like to thank Brett, the Flutter team, and the GSoC team for that. + +*About the author: Abdullah is a fresh computer engineering graduate based in Pune, India. He has been building mobile apps over the past 4 years and enjoys working with things built around it. You can connect with him on [Twitter](https://twitter.com/abdullahzakir99), [LinkedIn](https://www.linkedin.com/in/abdullahzakir99/), and [GitHub](https://github.com/abd99).* \ No newline at end of file diff --git a/sites/www/content/blog/hamilton-flare-design-challenge/images/0_uc59w_DpnfksbT1.jpg b/sites/www/content/blog/hamilton-flare-design-challenge/images/0_uc59w_DpnfksbT1.jpg new file mode 100644 index 0000000000..69f414bb35 Binary files /dev/null and b/sites/www/content/blog/hamilton-flare-design-challenge/images/0_uc59w_DpnfksbT1.jpg differ diff --git a/sites/www/content/blog/hamilton-flare-design-challenge/images/0utEEfftXnEdaQlEF.webp b/sites/www/content/blog/hamilton-flare-design-challenge/images/0utEEfftXnEdaQlEF.webp new file mode 100644 index 0000000000..c4ef9c3833 Binary files /dev/null and b/sites/www/content/blog/hamilton-flare-design-challenge/images/0utEEfftXnEdaQlEF.webp differ diff --git a/sites/www/content/blog/hamilton-flare-design-challenge/images/0wAn4IQgexh-pkr6K.webp b/sites/www/content/blog/hamilton-flare-design-challenge/images/0wAn4IQgexh-pkr6K.webp new file mode 100644 index 0000000000..7bb384edb1 Binary files /dev/null and b/sites/www/content/blog/hamilton-flare-design-challenge/images/0wAn4IQgexh-pkr6K.webp differ diff --git a/sites/www/content/blog/hamilton-flare-design-challenge/images/1N2BgtvNZQuZtG1ZpzNoxcA.gif b/sites/www/content/blog/hamilton-flare-design-challenge/images/1N2BgtvNZQuZtG1ZpzNoxcA.gif new file mode 100644 index 0000000000..7049693203 Binary files /dev/null and b/sites/www/content/blog/hamilton-flare-design-challenge/images/1N2BgtvNZQuZtG1ZpzNoxcA.gif differ diff --git a/sites/www/content/blog/hamilton-flare-design-challenge/index.md b/sites/www/content/blog/hamilton-flare-design-challenge/index.md new file mode 100644 index 0000000000..25d74d4003 --- /dev/null +++ b/sites/www/content/blog/hamilton-flare-design-challenge/index.md @@ -0,0 +1,131 @@ +--- +title: "Hamilton Flare Design Challenge" +description: "The Flutter community continues to impress us and we constantly see new initiatives to help developers from around the world learn and…" +publishDate: 2019-07-18 +author: aguinis +image: images/0wAn4IQgexh-pkr6K.webp +category: events +layout: blog +--- + +The Flutter community continues to impress us. We often see new initiatives to help developers from around the world learn and have fun with Flutter. [VeryGoodVentures](https://verygood.ventures/) just announced their first ever [Flare](https://www.2dimensions.com/about-flare) design challenge for the [Hamilton App](https://hamiltonmusical.com/app/), in partnership with [2Dimensions](https://www.2dimensions.com/). Check out [the official page](https://vgv.dev/hamappflare) of this contest for more details. + +<DashImage figure src="images/0wAn4IQgexh-pkr6K.webp" /> + + +Hamilton was [one of the first apps built with Flutter](https://medium.com/flutter/video-hamilton-app-built-in-3-months-with-flutter-reaches-1m-installs-f37f81856be4). The app was built in 3 months, reached 1M+ installs, and was featured on the Apple App and Google Play stores. Here is a video we made about the app: + +<YoutubeEmbed id="kfd-oLypqFI" title="Hamilton app built with Flutter and featured on iOS and Android (Flutter Developer Story)" fullwidth="true"/> + + +Flare is a new design and animation tool that allows users to build real, interactive, animated assets that run in real time in their final product. What better way to explore the potential of Flare for the Hamilton app than to open it up to designers, developers, and Hamilton fans. + +## #HAMAPPFLARE + +<DashImage figure src="images/1N2BgtvNZQuZtG1ZpzNoxcA.gif" /> + + +**The #HamAppFlare Challenge is a unique opportunity to win prizes and be featured in the Hamilton app (built with Flutter) while learning to use Flare.** + +This is your chance to create amazing animations with Flare and Flutter using the Hamilton app as your muse. Think of fun, interesting, and useful ways that animations could improve the Hamilton app and bring them to life with Flare. Or, just make something awesome with Flare that expresses your love for Hamilton. + +## To participate: + +1. Create a Flare animation that is suitable for Hamilton. + +1. Tweet a link to your submission using **#HamAppFlare.** + +1. [Submit your entry to the submission site](https://forms.gle/hi3uvLWPaFMP8aws9) by **August 15, 2019 at 11:59pm EST.** + +1. A winner will be selected by Hamilton, Very Good Ventures, and 2Dimensions. + +## The winner will receive: + +* $250 Hamilton merchandise pack + +* $250 Broadway.com gift card + +* 1-year Flare Subscription ($250) + Flare T-Shirt + +* In-app Winner Announcement + +* Social Mentions by @HamiltonMusical + +[ENTER THE CHALLENGE HERE](https://docs.google.com/a/google.com/forms/d/e/1FAIpQLSfi-U9QEdDuBFl1vlcIKFeqOoR-K2ePf-wEBjYrvUf4A3byig/closedform) + +**The Hamilton Flare Challenge opens on 7/20/19 and ends on 8/15/19 at 11:59pm EST.** + +## Hamilton — Rise Up With Flutter & Flare! + +<DashImage figure src="images/0_uc59w_DpnfksbT1.jpg" /> + + +In 2017, the hit Broadway musical, Hamilton, was the first brand outside of Google to launch a production app using [Flutter](https://flutter.dev/). The Hamilton app has been the [flagship Flutter app](https://www.youtube.com/watch?v=kfd-oLypqFI) since its launch with over 3 million installs globally. + +While Flutter is now well-known among developers as a game-changing portable UI framework, Hamilton embraced Flutter extremely early — in its pre-alpha stages. Hamilton and Very Good Ventures remain committed to building revolutionary app experiences with Flutter…and now Flare. + +## Details + +### Submission Guidelines + +Official details and rules can be found on the [submission site](https://forms.gle/hi3uvLWPaFMP8aws9). Any information below is for convenience only and is not “official.” + +* Submissions will be accepted from 7/20/2019 at 12:00am EST to 8/15/2019 at 11:59pm EST. + +* Submissions MUST include Flare files. Ideally Flare animations are also utilized in Flutter code that you provide. + +* Your submission must be made with a valid URL to a public repo on [Github](https://github.com/) or the [2Dimensions Flare site](https://www.2dimensions.com/explore). + +* Each submission must be accompanied by an MIT license: [https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT) + +* Your entry must not be offensive or mean-spirited — this is for fun and to encourage designers and developers to use Flare! + +### How will the submissions be judged? + +Flare submission will be judged by members of the Hamilton, Very Good Ventures, and Flare teams based on: + +* **Originality:** The uniqueness and novelty of the animation and its usage. + +* **Brand Alignment:** The degree to which the animation is on-brand for Hamilton and suitable for use in the app. + +* **Wow:** The degree to which it puts a smile on faces and showcases the power of Flare. + +### Spread the Word! + +The official hashtag for the challenge is **#HamAppFlare**. Please be sure to tweet or ‘gram your work and mention [@HamiltonMusical](https://twitter.com/hamiltonmusical), [@VGVentures](https://twitter.com/vgventures), [@2Dimentions](https://twitter.com/2dimensions), and [@FlutterDev](https://twitter.com/flutterdev)! + +### What is in the Hamilton Merch Pack? + +* Hamilton Gold Star T-Shirt — [link](https://broadwaymerchandiseshop.com/stores/hamilton/apparel/hamilton-gold-star-t-shirt) + +* Hamilton Baseball Cap — [link](https://broadwaymerchandiseshop.com/stores/hamilton/apparel/a-ham-hat-631) + +* Hamilton Pint Glass — [link](https://broadwaymerchandiseshop.com/stores/hamilton/souvenirs/hamilton-pint-glass) + +* Hamilton The Revolution Hardcover Book — [link](https://broadwaymerchandiseshop.com/stores/hamilton/media/hamilton-the-revolution-hardcover-book) + +* Hamilton Canvas Beach Bag — [link](https://broadwaymerchandiseshop.com/stores/hamilton/souvenirs/hamilton-canvas-beach-tote) + +* Hamilton Beach Towel — [link](https://broadwaymerchandiseshop.com/stores/hamilton/souvenirs/hamilton-beach-towel) + +* Hamilton Souvenir Program Book — [link](https://broadwaymerchandiseshop.com/stores/hamilton/souvenirs/program-book) + +* Hamilton Lapel Pin — [link](https://broadwaymerchandiseshop.com/stores/hamilton/shop-all/star-lapel-pin) + +### Looking for Ideas? + +Need help finding something inspired by the Hamilton app to create with Flare? Try some of these focus areas: + +* Enter lottery animations + +* “You won!” animations + +* Lottery tour location selector + +* Trivia star burst animations + +* Today screen content animations + +Really any button, content, or interaction is fair game! Or just create something fun using Hamilton and its characters as your muse — think of it as interactive fan art! + +<DashImage figure src="images/0utEEfftXnEdaQlEF.webp" /> diff --git a/sites/www/content/blog/handling-404-page-not-found-error-in-flutter/images/1eU-430nepv8cz3OYLIycng.webp b/sites/www/content/blog/handling-404-page-not-found-error-in-flutter/images/1eU-430nepv8cz3OYLIycng.webp new file mode 100644 index 0000000000..5567cfb038 Binary files /dev/null and b/sites/www/content/blog/handling-404-page-not-found-error-in-flutter/images/1eU-430nepv8cz3OYLIycng.webp differ diff --git a/sites/www/content/blog/handling-404-page-not-found-error-in-flutter/index.md b/sites/www/content/blog/handling-404-page-not-found-error-in-flutter/index.md new file mode 100644 index 0000000000..68893c5ab1 --- /dev/null +++ b/sites/www/content/blog/handling-404-page-not-found-error-in-flutter/index.md @@ -0,0 +1,55 @@ +--- +title: "Handling 404: Page not found error in Flutter" +description: "Learn how to handle 404 errors and create a custom page not found error page in your Flutter web application." +publishDate: 2020-06-10 +author: josealba +image: images/1eU-430nepv8cz3OYLIycng.webp +category: tutorial +layout: blog +--- + +<DashImage figure src="images/1eU-430nepv8cz3OYLIycng.webp" /> + + +Flutter has released web support that allows you to create dynamic websites. Flutter supports handling any error that might come from web usage such as 404 errors. But, how do you make a custom 404 page for your Flutter application? + +This article describes how to create custom pages that are shown anytime a user navigates to a page that doesn’t exist. + +## How to redirect clients to a custom 404 page in Flutter + +Everyone has encountered a “404: Page not found error” when browsing the internet. Flutter handles this issue by automatically redirecting you to the initial route. This is usually the home page of your application. But what if you want to have a fancy 404 page like the ones at [AirBnb](https://36bvmt283fg61unuud3h7qua-wpengine.netdna-ssl.com/wp-content/uploads/2013/03/airbnb-404.gif), [GitHub](https://mamchenkov.net/wordpress/wp-content/uploads/2013/11/github-404.png), or even the [Flutter website](https://flutter.dev/foo)? You can do this easily with Flutter. + +To create a custom 404 page, your application needs to use the `MaterialApp`, `CupertinoApp`, or `WidgetsApp` widget. Most applications use one of these three widgets; it is the first widget you call when creating a Flutter application. + +The MaterialApp configures the top-level Navigator to search for routes in the following order: + +1. For the `/` route, the [`home`](https://api.flutter.dev/flutter/material/MaterialApp/home.html) property, if non-null, is used. + +1. Otherwise, the [`routes`](https://api.flutter.dev/flutter/material/MaterialApp/routes.html) table is used, if it has an entry for the route. + +1. Otherwise, [`onGenerateRoute`](https://api.flutter.dev/flutter/material/MaterialApp/onGenerateRoute.html) is called, if provided. It should return a non-null value for any valid route not handled by [`home`](https://api.flutter.dev/flutter/material/MaterialApp/home.html) and [`routes`](https://api.flutter.dev/flutter/material/MaterialApp/routes.html). + +1. Finally if all else fails [`onUnknownRoute`](https://api.flutter.dev/flutter/material/MaterialApp/onUnknownRoute.html) is called. + +If your route isn’t handled in any of these tables then it uses the [`onUnknownRoute`](https://api.flutter.dev/flutter/material/MaterialApp/onUnknownRoute.html) property to handle your navigation. This callback is typically used for error handling. For example, this function might always generate a “not found” page that describes the route that wasn’t found. Unknown routes can arise either from errors in the app or from external requests to push routes, such as from Android intents. + +The following example code demonstrates how to define an anonymous function for the `onUnknownRoute` property, which takes a `RouteFactory`, a factory method that takes a `RouteSettings function` as input and returns a `Route`. The following snippet shows how simple it can be to define the `onUnknownRoute` property: + +``` +onUnknownRoute: (settings) { + + return MaterialPageRoute(builder: (_) => PageNotFound()); + +}, +``` + + +PageNotFound is a custom widget that creates the 404 page . This page might explain what happened and redirect the user to the home page, but you can be as creative as you want when creating your 404 your page. + +## Closing Remarks + +When creating a Flutter application it is important to handle any issues that might arise. Using the `onUnknownRoute` property within `MaterialApp`, `CupertinoApp`, or `WidgetApp` lets you handle the inevitable “page not found” errors in your website. + +To learn more about routes in Flutter, see the Medium post [“Navigating URLs using named routes”](https://medium.com/flutter/flutter-web-navigating-urls-using-named-routes-307e1b1e2050). + +*About the author: Jose recently graduated from university and now works on [Material](https://material.io), a design system that helps teams build high-quality digital experiences. Jose’s team maintains the Flutter [material library](https://api.flutter.dev/flutter/material/material-library.html). To learn more, visit his Jose’s pages on [GitHub](https://github.com/JoseAlba), [LinkedIn](https://www.linkedin.com/in/josealba1996/), [YouTube](https://www.youtube.com/channel/UCOdKA_On0oPe1tz02z1QfxA?view_as=subscriber), and [Instagram](https://www.instagram.com/jose.alba/).* \ No newline at end of file diff --git a/sites/www/content/blog/handling-web-gestures-in-flutter/images/1BzcKF4qdbLIFFB6XL8x-9A.webp b/sites/www/content/blog/handling-web-gestures-in-flutter/images/1BzcKF4qdbLIFFB6XL8x-9A.webp new file mode 100644 index 0000000000..53c4b73f62 Binary files /dev/null and b/sites/www/content/blog/handling-web-gestures-in-flutter/images/1BzcKF4qdbLIFFB6XL8x-9A.webp differ diff --git a/sites/www/content/blog/handling-web-gestures-in-flutter/index.md b/sites/www/content/blog/handling-web-gestures-in-flutter/index.md new file mode 100644 index 0000000000..e00ddf7582 --- /dev/null +++ b/sites/www/content/blog/handling-web-gestures-in-flutter/index.md @@ -0,0 +1,275 @@ +--- +title: "Handling Web Gestures in Flutter" +description: "Flutter has released web support that allows you to create dynamic websites. With Flutter web, MacOS, Linux, and Windows operating systems…" +publishDate: 2020-09-21 +author: josealba +image: images/1BzcKF4qdbLIFFB6XL8x-9A.webp +category: announcements +layout: blog +--- + +## Handling web gestures in Flutter + +<DashImage figure src="images/1BzcKF4qdbLIFFB6XL8x-9A.webp" /> + + +Flutter has released web support that allows you to create dynamic websites. With Flutter web, MacOS, Linux, and Windows operating systems (even on mobile devices connected to a mouse, keyboard, or other peripherals), you now have access to platform-specific behaviors including focus support, keyboard input, custom mouse cursors, and hover input. But, how do you make use of these platform behaviors in Flutter? + +This article explains how to use the `FocusableActionDetector` widget, which handles keyboard input, focus input, hover input, and custom mouse cursors. + +## How to use the Focusable Action Detector widget + +The Focusable Action Detector widget allows you to handles several web-specific actions, including: + +1. Keyboard input + +1. Focus input + +1. Hover input + +1. Custom mouse cursors + +See the `FocusableActionDetector` in action in the [DartPad #1](https://dartpad.dev/d16345202d0e26d40fe14904657dc24a) example. +> **Note**: This article uses two DartPad examples, though there are multiple links to each. [DartPad #1](https://dartpad.dev/d16345202d0e26d40fe14904657dc24a) shows the `FocusableActionDetector` sample, and [DartPad #2](https://dartpad.dev/5544c57e20454fe212e3ec5cf10b1f0a) demonstrates hover and focus support for `FocusableActionDetector`. + +## Keyboard input + +Flutter lets you define custom actions to handle keyboard input. Let’s say you want to have a hidden menu or your application has to handle a lot of custom keyboard input. This is easy to achieve with the `FocusableActionDetector` widget. + +First, add the following import: + +`import ‘package:flutter/services.dart’;` + +Next, from the class where the `FocusableActionDetector` is called, initialize two private variables. These variables map the keyboard input to the desired action: + +``` +Map<LogicalKeySet, Intent> _shortcutMap; + +Map<Type, Action<Intent>> _actionMap; +``` + + +Define a `FocusableActionDetector` widget. Note that the custom keyboard input activates only when the child widget is in focus: + +``` +FocusableActionDetector( + + actions: _actionMap, + + shortcuts: _shortcutMap, + + child: + +) +``` + + +This class maps the enums and keyboard input type to the `Intent` class. The following setup is necessary because the actions and shortcuts parameter requires a class that extends `Intent`: + +``` +class _ShowSecretMessageIntent extends Intent { + + const _ShowSecretMessageIntent({@required this.type}); + + const _ShowSecretMessageIntent.P() : type = _SecretMessageType.P; + + const _ShowSecretMessageIntent.L() : type = _SecretMessageType.L; + + const _ShowSecretMessageIntent.A() : type = _SecretMessageType.A; + + const _ShowSecretMessageIntent.T() : type = _SecretMessageType.T; + + const _ShowSecretMessageIntent.F() : type = _SecretMessageType.F; + + const _ShowSecretMessageIntent.O() : type = _SecretMessageType.O; + + const _ShowSecretMessageIntent.R() : type = _SecretMessageType.R; + + const _ShowSecretMessageIntent.M() : type = _SecretMessageType.M; + + final _SecretMessageType type; + +} + +enum _SecretMessageType { + + P, + + L, + + A, + + T, + + F, + + O, + + R, + + M, + +} +``` + + +Within the `initState` method, initialize the mapping previously defined: + +``` +void initState() { + + _shortcutMap = <LogicalKeySet, Intent>{ + + LogicalKeySet(LogicalKeyboardKey.keyP): + + const _ShowSecretMessageIntent.P(), + + LogicalKeySet(LogicalKeyboardKey.keyL): + + const _ShowSecretMessageIntent.L(), + + LogicalKeySet(LogicalKeyboardKey.keyA): + + const _ShowSecretMessageIntent.A(), + + LogicalKeySet(LogicalKeyboardKey.keyT): + + const _ShowSecretMessageIntent.T(), + + LogicalKeySet(LogicalKeyboardKey.keyF): + + const _ShowSecretMessageIntent.F(), + + LogicalKeySet(LogicalKeyboardKey.keyO): + + const _ShowSecretMessageIntent.O(), + + LogicalKeySet(LogicalKeyboardKey.keyR): + + const _ShowSecretMessageIntent.R(), + + LogicalKeySet(LogicalKeyboardKey.keyM): + + const _ShowSecretMessageIntent.M(), + + }; + + _actionMap = <Type, Action<Intent>>{ + + _ShowSecretMessageIntent: CallbackAction<_ShowSecretMessageIntent>( + + onInvoke: _actionHandler, + + ), + + }; + +} +``` + + +The action handler links the keyboard input to a certain function within the application. Any time a character is pressed, an expanded container is added to the main part of the application. But in your application, you can use it for more creative purposes: + +``` +void _actionHandler(_ShowSecretMessageIntent intent) { + + switch (intent.type) { + + case _SecretMessageType.P: + + setState(() { + + children.add( + + Expanded( + + child: Container( + + color: Colors.orange, + + child: Text(‘Press L’), + + ), + + ), + + ); + + }); + + break; + + … + + } + +} +``` + + +## Focus input + +The `FocusableActionDetector` widget lets you perform action calls any time the specified widget is in focus. + +The focus tree allows you to navigate through your application using the tab key. This allows people with visual impairment to navigate through your website. + +The `FocusableActionDetector` child widget is selected as the initial focus when no other node in its scope is currently focused. It’s important to set the `autofocus` parameter variable to `true` if you want your custom keyboard input to work regardless of whether your child widget has focus or not. This is because, by default, this `FocusableActionDetector` widget won’t be in focus, hence you won’t be able to use custom keyboard inputs: + +``` +FocusableActionDetector( + + autofocus: true, + + child : … + +), +``` + + +Let’s say you want your child widget to have a different color when focused to identify that this widget is currently in focus. If you need to set custom actions whenever your widget has focus, use the `onShowFocusHighlight` property. + +The [DartPad #2](https://dartpad.dev/5544c57e20454fe212e3ec5cf10b1f0a) example shows how to use the `onShowFocusHighlight` property. + +Focus is an advanced topic. Learn more about focus by reading the API docs for the [`Focus`](https://api.flutter.dev/flutter/widgets/Focus-class.html) class + +## Hover input + +The `FocusableActionDetector` widget lets you handle hover input. That way, anytime a cursor hovers over a certain widget, custom actions can happen. Similar to the `onShowFocusHighlight` property, the `onShowHoverHighlight` property lets you create custom actions anytime the mouse cursor hovers over a certain widget. + +Check out the [DartPad #2](https://dartpad.dev/5544c57e20454fe212e3ec5cf10b1f0a) example, which demonstrates how to implement custom hover and focus action. + +In the [DartPad #2](https://dartpad.dev/5544c57e20454fe212e3ec5cf10b1f0a) example, anytime the mouse hovers over the widget it changes the color of the widget. If you are navigating the application using tab, then you’ll notice that the widget in focus applies a different shade to the widget compared to the widget that is currently being hovered over. + +This means you can have different custom actions anytime the widget is in focus or a mouse cursor hovers over that widget. You can tell this if you hover and focus over on the same widget. That widget is applying two shades at the same time to that widget. For more information, see the API docs for the [`FocusableActionDetector`](https://api.flutter.dev/flutter/widgets/FocusableActionDetector-class.html) class. + +## Custom mouse cursors + +The `FocusableActionDetector` widget lets you implement custom mouse cursors. For example, if you want your users to know that a widget is draggable, changing the mouse cursor is a good indicator that you can do this. The `FocusableActionDetector` widget has a `mouseCursor` parameter. + +`mouseCursor: SystemMouseCursors.grabbing` + +The [DartPad #1](https://dartpad.dev/d16345202d0e26d40fe14904657dc24a) example demonstrates the different types of mouse cursors within Flutter. Click any of the icons in the app bar to try. There are nine different system default mouse cursors: + +1. `SystemMouseCursors.basic` + +1. `SystemMouseCursors.text` + +1. `SystemMouseCursors.click` + +1. `SystemMouseCursors.forbidden` + +1. `SystemMouseCursors.grab` + +1. `SystemMouseCursors.grabbing` + +1. `SystemMouseCursors.horizontalDoubleArrow` (not in stable) + +1. `SystemMouseCursors.verticalDoubleArrow` (not in stable) + +1. `SystemMouseCursors.none` + +## Closing remarks + +In Flutter you have access to web-specific APIs. The `FocusableActionDetector` widget lets you handle web-specific functions in Flutter. To learn more about Flutter web, see the Medium post, [Handling 404: Page not found error in Flutter](https://medium.com/flutter/handling-404-page-not-found-error-in-flutter-731f5a9fba29). + +*About the author: Jose recently graduated from university and now works on [Material](https://material.io/), a design system that helps teams build high-quality digital experiences. Jose’s team maintains the Flutter [material library](https://api.flutter.dev/flutter/material/material-library.html). To learn more, visit his Jose’s pages on [GitHub](https://github.com/JoseAlba), [LinkedIn](https://www.linkedin.com/in/josealba1996/), [YouTube](https://www.youtube.com/channel/UCOdKA_On0oPe1tz02z1QfxA?view_as=subscriber), and [Instagram](https://www.instagram.com/jose.alba/).* \ No newline at end of file diff --git a/sites/www/content/blog/harness-the-gemini-api-in-your-dart-and-flutter-apps/images/07Zvr0YiN7O22wTOy.webp b/sites/www/content/blog/harness-the-gemini-api-in-your-dart-and-flutter-apps/images/07Zvr0YiN7O22wTOy.webp new file mode 100644 index 0000000000..919cf46047 Binary files /dev/null and b/sites/www/content/blog/harness-the-gemini-api-in-your-dart-and-flutter-apps/images/07Zvr0YiN7O22wTOy.webp differ diff --git a/sites/www/content/blog/harness-the-gemini-api-in-your-dart-and-flutter-apps/images/0xhBJ20OLOQDtQ2xi.webp b/sites/www/content/blog/harness-the-gemini-api-in-your-dart-and-flutter-apps/images/0xhBJ20OLOQDtQ2xi.webp new file mode 100644 index 0000000000..8a8631dbba Binary files /dev/null and b/sites/www/content/blog/harness-the-gemini-api-in-your-dart-and-flutter-apps/images/0xhBJ20OLOQDtQ2xi.webp differ diff --git a/sites/www/content/blog/harness-the-gemini-api-in-your-dart-and-flutter-apps/images/1-KkJmzvv3jNhh88TWxIBJg.webp b/sites/www/content/blog/harness-the-gemini-api-in-your-dart-and-flutter-apps/images/1-KkJmzvv3jNhh88TWxIBJg.webp new file mode 100644 index 0000000000..642aec3e8c Binary files /dev/null and b/sites/www/content/blog/harness-the-gemini-api-in-your-dart-and-flutter-apps/images/1-KkJmzvv3jNhh88TWxIBJg.webp differ diff --git a/sites/www/content/blog/harness-the-gemini-api-in-your-dart-and-flutter-apps/index.md b/sites/www/content/blog/harness-the-gemini-api-in-your-dart-and-flutter-apps/index.md new file mode 100644 index 0000000000..7d98cda458 --- /dev/null +++ b/sites/www/content/blog/harness-the-gemini-api-in-your-dart-and-flutter-apps/index.md @@ -0,0 +1,119 @@ +--- +title: "Harness the Gemini API in your Dart and Flutter Apps" +description: "Introducing the Google AI Dart SDK" +publishDate: 2024-02-15 +author: anderdobo +image: images/1-KkJmzvv3jNhh88TWxIBJg.webp +category: spotlight +layout: blog +--- + +We’re thrilled to announce the launch of the Google AI Dart SDK for the Gemini API. The new pub.dev package, [`google_generative_ai`,](https://pub.dev/packages/google_generative_ai) and [supporting resources](https://ai.google.dev/tutorials/dart_quickstart) enable you to build your own generative AI-based features into Dart and Flutter apps through an idiomatic Dart integration with the Gemini API. It opens the door to a vast range of possibilities for building intelligent, performant applications for Android, iOS, web, macOS, Windows, and Linux from a single code base. + +With the Google AI Dart SDK, you can: + +* **Easily integrate generative AI features:** Add advanced text generation, summarization, chat, and more to your Dart or Flutter apps with minimal setup. + +* **Tap into Google’s most capable and general model yet:** The Gemini model draws on Google’s extensive research and development in machine learning, giving you access to generative AI capabilities that will continue to improve. + +* **Accelerate your AI-powered app development:** Focus on your app logic and user experience, while the SDK handles the intricacies of interacting with AI models. + +* **Build cross-platform AI-powered apps:** Easily create generative AI features across desktop, web, and mobile applications using Flutter. + +* **Use the Gemini API in more than 180+ countries and territories:** Check the [available regions](https://ai.google.dev/available_regions#available_regions) for the most current list of countries and regions where the Gemini API and Google AI Studio (described further below) are available. + +<DashImage figure src="images/1-KkJmzvv3jNhh88TWxIBJg.webp" /> + + +**What can you build?** + +We believe generative AI holds immense potential to help you achieve your app and business goals. And since the Gemini model is multimodal (it’s capable of processing information from multiple modalities, including images and text), it empowers you to be extremely creative. However, the first question we often get from app developers — and even from within our own team — is “What can I actually do with the Gemini API?” Here are a few examples of features you might create for your Dart or Flutter app: + +* **Text summarization:** Generate concise summaries of long articles, research papers, or website content from textual input. + +* **Smart chatbots:** Build more engaging and human-like conversational interfaces, enhancing user experience in your applications. + +* **Visual search engine:** Users can upload an image, and the app uses the Gemini API to return descriptions of what’s in the image, the style, and perhaps even how to make what’s in the image. + +* **Image descriptions for accessibility:** Generate detailed text descriptions of uploaded images to aid users who are visually impaired. + +* **Diagram & chart interpretation:** Users can upload images of diagrams, charts, or graphs, and the Gemini API delivers a text-based analysis and explanation of the data. + +This list could go on because the possibilities are nearly endless! + +<DashImage figure src="images/07Zvr0YiN7O22wTOy.webp" alt="A screenshot of the Flutter sample app that uses the Google AI Dart SDK" caption="A screenshot of the Flutter sample app that uses the Google AI Dart SDK" /> + + +**Getting Started** + +Check out the [Dart quickstart](https://ai.google.dev/tutorials/dart_quickstart) for a detailed step-by-step guide on how to get set up. At a high level, here’s what you’ll do: + +1. Get a Gemini API key from Google AI Studio. Keep this key secure. We strongly recommend that you do not include the key directly in your code, or check files that contain the key into version control systems. While developing, we recommend using `flutter run -d [DEVICE NAME] — dart-define=API_KEY=[YOUR API KEY]` to run the app in an emulator/simulator, using your API key as an environment variable. + +1. Add the Google AI Dart SDK to your Dart or Flutter app by running `dart pub add google_generative_ai` or `flutter pub add google_generative_ai`, respectively. This adds `google_generative_ai` as a dependency to your `pubspec.yaml` file. + +1. Initialize the generative model in your code: + +```dart +import 'package:google_generative_ai/google_generative_ai.dart'; + +// Access your API key as an environment variable (see first step above) +final apiKey = Platform.environment['API_KEY']; +if (apiKey == null) { + print('No \$API_KEY environment variable'); + exit(1); +} + +final model = GenerativeModel(model: 'MODEL_NAME', apiKey: apiKey); +``` + + +4. You can now start to explore using the Gemini API to implement different use cases. For example, when the prompt input includes both text and images, use the `gemini-pro-vision` model and the `generateContent` method to generate text output: + +```dart +import 'dart:io'; + +import 'package:google_generative_ai/google_generative_ai.dart'; + +void main() async { + // Access your API key as an environment variable (see first step above) + final apiKey = Platform.environment['API_KEY']; + if (apiKey == null) { + print('No \$API_KEY environment variable'); + exit(1); + } + // For text-and-image input (multimodal), use the gemini-pro-vision model + final model = GenerativeModel(model: 'gemini-pro-vision', apiKey: apiKey); + final (firstImage, secondImage) = await ( + File('image0.jpg').readAsBytes(), + File('image1.jpg').readAsBytes() + ).wait; + final prompt = TextPart("What's different between these pictures?"); + final imageParts = [ + DataPart('image/jpeg', firstImage), + DataPart('image/jpeg', secondImage), + ]; + final response = await model.generateContent([ + Content.multi([prompt, ...imageParts]) + ]); + print(response.text); +} +``` + + +Explore the [Gemini API documentation](https://ai.google.dev/docs) and check out the [Dart and Flutter sample apps](https://github.com/google/generative-ai-dart/tree/main/samples) in the GitHub repo for detailed guides and examples on how to use the SDK for various use cases, or in [this sample app](https://dartpad.dev/?id=341bc46b2ed1d2055d357ab987ed5fc2) in DartPad, which is a free, open-source online editor for Dart and Flutter snippets, now built with Flutter. Please report any issues or tell us about feature requests in the [generative-ai-dart GitHub repo](https://github.com/google/generative-ai-dart/issues/new/choose). + +**Google AI Studio** + +Alongside the SDK, [Google AI Studio](https://aistudio.google.com/?utm_source=flutter&utm_medium=referral&utm_campaign=blog_gaidartsdk_announcment&utm_content=) is a browser-based IDE for prototyping with generative models. It enables you to quickly iterate to develop prompts for your use case, and then get an API key to use in your app development. You can sign into Google AI Studio with your Google account and take advantage of the free quota, which allows 60 requests per minute. To help us improve product quality, when you use the free quota, your Google AI Studio input and output might be accessible to trained reviewers. This data is de-identified from your Google account and API key. + +We will add Dart to Google AI Studio soon, so keep a lookout for the announcement! This will enable you to simply click on “Get code”, select a new Dart tab (which will be alongside the existing supported languages), and then “Copy” the Dart code to transfer your work to your IDE of choice. + +<DashImage figure src="images/0xhBJ20OLOQDtQ2xi.webp" alt="Google AI Studio" caption="Google AI Studio" /> + + +**Share what you build!** + +We look forward to seeing what you’ll build with Gemini, like the team at LeanCode who have used the Gemini API to build [arb_translate](https://leancode.co/arb_translate). It’s a package that helps developers to perform language translation automatically, streamlining localization in Flutter apps. + +Use the hashtag #BuildWithGemini on Twitter/X to let us know what you’re building! \ No newline at end of file diff --git a/sites/www/content/blog/how-can-we-improve-the-flutter-experience-for-desktop/images/13nXqiZ_vAvOXBU4yeRTOHA.webp b/sites/www/content/blog/how-can-we-improve-the-flutter-experience-for-desktop/images/13nXqiZ_vAvOXBU4yeRTOHA.webp new file mode 100644 index 0000000000..de69ab955f Binary files /dev/null and b/sites/www/content/blog/how-can-we-improve-the-flutter-experience-for-desktop/images/13nXqiZ_vAvOXBU4yeRTOHA.webp differ diff --git a/sites/www/content/blog/how-can-we-improve-the-flutter-experience-for-desktop/images/1Ku1PCFW0lYew-Dh7pIGDvQ.webp b/sites/www/content/blog/how-can-we-improve-the-flutter-experience-for-desktop/images/1Ku1PCFW0lYew-Dh7pIGDvQ.webp new file mode 100644 index 0000000000..6ca8795e56 Binary files /dev/null and b/sites/www/content/blog/how-can-we-improve-the-flutter-experience-for-desktop/images/1Ku1PCFW0lYew-Dh7pIGDvQ.webp differ diff --git a/sites/www/content/blog/how-can-we-improve-the-flutter-experience-for-desktop/images/1VbmSF-liAOyeParju2Y4kw.webp b/sites/www/content/blog/how-can-we-improve-the-flutter-experience-for-desktop/images/1VbmSF-liAOyeParju2Y4kw.webp new file mode 100644 index 0000000000..f2a0b35057 Binary files /dev/null and b/sites/www/content/blog/how-can-we-improve-the-flutter-experience-for-desktop/images/1VbmSF-liAOyeParju2Y4kw.webp differ diff --git a/sites/www/content/blog/how-can-we-improve-the-flutter-experience-for-desktop/images/1kfqE355iFMpjF2r52wVviA.webp b/sites/www/content/blog/how-can-we-improve-the-flutter-experience-for-desktop/images/1kfqE355iFMpjF2r52wVviA.webp new file mode 100644 index 0000000000..08a8eb842a Binary files /dev/null and b/sites/www/content/blog/how-can-we-improve-the-flutter-experience-for-desktop/images/1kfqE355iFMpjF2r52wVviA.webp differ diff --git a/sites/www/content/blog/how-can-we-improve-the-flutter-experience-for-desktop/images/1tleEgMs6XOSPDo4smD7Zvg.webp b/sites/www/content/blog/how-can-we-improve-the-flutter-experience-for-desktop/images/1tleEgMs6XOSPDo4smD7Zvg.webp new file mode 100644 index 0000000000..be290fe0de Binary files /dev/null and b/sites/www/content/blog/how-can-we-improve-the-flutter-experience-for-desktop/images/1tleEgMs6XOSPDo4smD7Zvg.webp differ diff --git a/sites/www/content/blog/how-can-we-improve-the-flutter-experience-for-desktop/index.md b/sites/www/content/blog/how-can-we-improve-the-flutter-experience-for-desktop/index.md new file mode 100644 index 0000000000..f7f3fcb46f --- /dev/null +++ b/sites/www/content/blog/how-can-we-improve-the-flutter-experience-for-desktop/index.md @@ -0,0 +1,92 @@ +--- +title: "How can we improve the Flutter experience for desktop?" +description: "Insights and priorities based on 2022 Flutter desktop user research by Mindy Hoover and Chris Bracken" +publishDate: 2022-12-12 +author: melyndahoover +image: images/1VbmSF-liAOyeParju2Y4kw.webp +category: announcements +layout: blog +--- + +In February, Flutter launched official [support for developing Windows](https://medium.com/flutter/announcing-flutter-for-windows-6979d0d01fed) apps, extending its cross-platform capabilities to desktop. With the release of [Flutter 3](https://medium.com/flutter/introducing-flutter-3-5eb69151622f) in May 2022, Flutter announced stable support for macOS and Linux, completing the trifecta of desktop support! Now that Flutter users have had a few months to experiment with these new capabilities, we wanted to know: *how can Flutter better support desktop developers?* + +To answer that question, we collected data from 1,901 Flutter desktop developers in the Q3 Flutter survey. If you attended Flutter Vikings in August, you might have seen UX researchers chatting with developers during focus group sessions as well. Here’s what we found and how we’ll use it to improve Flutter. + +## How satisfied are you with Flutter on desktop? + +On average 78% of Flutter desktop developers across Windows, macOS, and Linux reported that they were *very* or *somewhat satisfied* with their development experience. This rating is on par with satisfaction for iOS (78%) and in between Android (92%) and web (63%). That’s great news for Flutter desktop! But it also begs the question, where is the experience falling short for the other 22%? + +<DashImage figure src="images/13nXqiZ_vAvOXBU4yeRTOHA.webp" alt="Satisfaction by target platform" caption="Satisfaction by target platform" /> + + +## What parts of the desktop developer workflow are most challenging? + +Next, we asked developers to gauge their satisfaction at various stages of the software development workflow. The results showed that satisfaction tended to decrease over the course of the desktop app development process. This resulted in the lowest user satisfaction while *debugging performance issues* and *deploying the app*. Additionally, finding Flutter packages that work for desktop is challenging, according to qualitative feedback from the survey and focus groups. + +<DashImage figure src="images/1VbmSF-liAOyeParju2Y4kw.webp" alt="Flutter for desktop satisfaction by development workflow stage" caption="Flutter for desktop satisfaction by development workflow stage" /> + + +The Flutter ecosystem needs more desktop-enabled packages to better support desktop. (The most requested packages are listed later in this article.) We are hard at work on new desktop-enabled APIs (more on that below), but we can’t do it all. We hope that the community will also step up and help fill those gaps by creating new packages and adding desktop support to existing ones using [federated plugins](https://docs.flutter.dev/development/packages-and-plugins/developing-packages#federated-plugins), for example. + +We know that troubleshooting performance issues isn’t a fun task, but it should at least be a satisfying experience. Our team is working hard to improve the functionality of Flutter DevTools, especially the memory analysis features, to make troubleshooting performance easier. For example, now you can use Flutter’s DevTools to [compare memory snapshots](https://docs.flutter.dev/development/tools/devtools/release-notes/release-notes-2.20.0)! Also, see the completely rewritten [memory view page](https://docs.flutter.dev/development/tools/devtools/memory). + +Flutter desktop developers told us that deploying desktop apps requires too many steps. The desktop team will be conducting a task analysis on desktop app deployment to determine where we can improve app deployment processes and documentation. + +## What would help you make better desktop user interfaces? + +72% of users agreed that “Flutter’s built-in widgets can create UIs that are suitable for desktop.” When we asked developers what would help them improve the UI design of their desktop apps, they overwhelmingly replied that they wanted better support for: + +1. Adaptive layouts (44%) + +1. Drag and drop (31%) + +1. Keyboard shortcuts (27%) + +<DashImage figure src="images/1kfqE355iFMpjF2r52wVviA.webp" alt="Which of the following could be improved to help you reach your design goals when using flutter to develop desktop apps?" caption="Which of the following could be improved to help you reach your design goals when using flutter to develop desktop apps?" /> + + +We strive to make Flutter the best *cross-platform* app development framework, so this is always top of mind. However, improving “*adaptive layouts*” is an ambiguous problem space that affects every target platform, not just desktop. There are also many factors at play including screen size, operating system, input devices (to name a few), so designing a useful solution isn’t a trivial task. But our engineers are on the job, working on improvements to the [flutter_adapative_scaffold](https://pub.dev/packages/flutter_adaptive_scaffold) package and creating codelabs to walk you through how to efficiently build adaptive apps. In the meantime, stay tuned for [Flutter Forward](https://flutter.dev/events/flutter-forward) in January, where Greg Spencer and Justin McCandless will be discussing how to architect adaptive apps using existing Flutter tools. + +As for drag and drop and keyboard shortcuts, they have both been on our radar for a while now. In fact, the [Shortcuts Widget](https://api.flutter.dev/flutter/widgets/Shortcuts-class.html) was recently released. You can see it in action in this recent “[Widget of the Week” episode](https://www.youtube.com/watch?v=6ZcQmdoz9N8). Finally, support for drag and drop is one of our top priorities in 2023! + +## What packages would make Flutter desktop development simpler? + +Package support for desktop is still catching up to that of mobile and web. The most requested packages from desktop developers were: + +1. multi-window (35%) + +1. native controls (32%) + +1. local storage (24%) + +1. web views (23%) + +<DashImage figure src="images/1tleEgMs6XOSPDo4smD7Zvg.webp" alt="Which APIs or features do you wish were better supported by Flutter?" caption="Which APIs or features do you wish were better supported by Flutter?" /> + + +This feedback was closely aligned with Flutter’s current list of priorities with APIs for multi-window, native menus, and web views in the pipeline. Once those are released we can begin to prioritize solutions to local data storage and database access. Some additional package requests that are currently lower on the priority list, but also warrant consideration, are audio/video players and audio/video devices. A need for packages that support clipboard management and complex input devices (3D mouse, gamepad controllers, for example) came up during focus groups at Flutter Vikings, but wasn’t included in the quarterly survey. + +If you are interested in contributing to Flutter packages for desktop, consider helping grow the Flutter ecosystem by creating packages to fill these gaps or submitting PRs to existing packages. By working together, we are confident the Flutter package ecosystem will grow and adapt to better support desktop developers. + +## What additional desktop documentation is needed? + +The last category surveyed covered Flutter’s desktop documentation. Satisfaction with Flutter’s desktop docs was 13% lower than satisfaction with the rest of Flutter’s documentation in Q3. Next, we asked what documentation would help streamline the desktop development process. The most requested documentation topics were: + +1. Publishing apps (43%) + +1. Automatic updating (42%) + +1. Foreign Function Interface (FFI) (42%) + +This feedback has been very helpful for prioritizing documentation work in Q4 2022 and 2023. As a result, our team is currently drafting documentation to help desktop developers publish their apps and including a helpful [codelab](https://github.com/flutter/codelabs/tree/main/ffigen_codelab) that is nearly complete! Although automatic updating is not a feature we maintain for Flutter, there are a number of [community](https://pub.dev/packages/auto_updater) [packages](https://pub.dev/packages/squirrel) in the Flutter ecosystem that provide this capability. Therefore, it’s not high on our priority list to provide an additional package. Please see [pub.dev](http://pub.dev) for automatic updating packages and their associated documentation. + +<DashImage figure src="images/1Ku1PCFW0lYew-Dh7pIGDvQ.webp" alt="Which of the following desktop-specific topics do you wish was better documented?" caption="Which of the following desktop-specific topics do you wish was better documented?" /> + + +## What’s next? + +In 2023, the Flutter team is prioritizing work based on your feedback. We are actively working on solutions for multi-window and native menu bars. Drag and drop is also high on our priority list. A task analysis is in the works to determine how to make app deployment more efficient; additional documentation on this topic is forthcoming. We are also hard at work making improvements to Flutter DevTools so you can better monitor app performance and fix memory leaks. Lastly, we are creating codelabs and investigating improvements to the flutter_adaptive_scaffold package to help developers build adaptive, cross-platform apps with Flutter. + +Now that we’ve set the bar, we’ll be enacting these improvements, and checking in with Flutter developers to measure our success. So keep an eye out for future Flutter surveys! + +Thank you to everyone who contributed their desktop feedback during the Q3 Flutter survey and to those who took the time to talk to us at Flutter Vikings! The value of your feedback is immeasurable. If you would like to participate in future studies beyond the quarterly surveys, you can [sign up for upcoming UX studies](https://docs.google.com/forms/d/e/1FAIpQLSe0i4De809KXVCdljGKrjMj3lxhuzbuFKCtY5PEQPCYtGxFMg/viewform). \ No newline at end of file diff --git a/sites/www/content/blog/how-dart-and-flutter-are-thinking-about-ai-in-2026/images/1acnlJyV5J3HU4jFbo2LQog.gif b/sites/www/content/blog/how-dart-and-flutter-are-thinking-about-ai-in-2026/images/1acnlJyV5J3HU4jFbo2LQog.gif new file mode 100644 index 0000000000..a69e96c2b8 Binary files /dev/null and b/sites/www/content/blog/how-dart-and-flutter-are-thinking-about-ai-in-2026/images/1acnlJyV5J3HU4jFbo2LQog.gif differ diff --git a/sites/www/content/blog/how-dart-and-flutter-are-thinking-about-ai-in-2026/index.md b/sites/www/content/blog/how-dart-and-flutter-are-thinking-about-ai-in-2026/index.md new file mode 100644 index 0000000000..458b1c294a --- /dev/null +++ b/sites/www/content/blog/how-dart-and-flutter-are-thinking-about-ai-in-2026/index.md @@ -0,0 +1,53 @@ +--- +title: "How Dart and Flutter are thinking about AI in 2026" +description: "Last month, we published Flutter and Dart’s 2026 Roadmap, a peek into how our teams are approaching our core mission: Building the most…" +publishDate: 2026-04-01 +author: mariam_hasnany +image: images/1acnlJyV5J3HU4jFbo2LQog.gif +layout: blog +--- + +<DashImage figure src="images/1acnlJyV5J3HU4jFbo2LQog.gif" alt="Dash imagines the future" caption="Dash imagines the future" /> + + +Last month, we published [Flutter and Dart’s 2026 Roadmap](https://blog.flutter.dev/flutter-darts-2026-roadmap-89378f17ebbd), a peek into how our teams are approaching our core mission: Building the most popular, fastest growing, and highest-productivity multi-platform UI framework. But it’s impossible to talk about development in 2026 without acknowledging AI. + +To meet this moment, today we are sharing our data-driven and honest thoughts on *how* our teams approach Dart and Flutter + AI in 2026. This post is intended to be less of a roadmap and more of a collection of thoughts guiding our strategy. Transparency is a core pillar of Flutter, and as an open source project, we believe in the value of experimentation in public. Interpret this as a snapshot of how we’re thinking about AI today; we value your input and feedback but we also recognize that, in this rapidly shifting environment, strategy and approach may change as models, tools, and their use evolve. + +## The Agentic shift by the numbers + +Flutter gained initial traction by staying ahead of the curve and adapting nimbly to the needs of developers building cross-platform. Now, AI represents another variable in how we think about our ecosystem. Not because every developer needs to use AI, but because we need to consider how it’s shifting our goals for those who do. + +AI adoption is no longer a future trend; it is the current reality. The [2025 Stack Overflow Developer Survey](https://survey.stackoverflow.co/2025/ai) showed that 84% of all developers use AI tools in some way within their daily workflows, and in our [2025 Flutter User Survey](https://docs.flutter.dev/reference/user-surveys), we found that 79% of Flutter developers specifically report using AI assistants while working with Flutter. + +However, this rapid adoption has revealed a significant “trust gap.” While [73% of developers feel more productive](https://github.blog/news-insights/research/research-quantifying-github-copilots-impact-on-developer-productivity-and-happiness/#finding-1-developer-productivity-goes-beyond-speed) because they can reduce the friction of starting new tasks, [46% do not yet trust the accuracy](https://survey.stackoverflow.co/2025/ai#developer-tools-ai-acc-ai-acc) of AI tools for critical tasks like writing, debugging, or fixing code. This leads to a “verification tax” — the time engineers pay auditing the output or fine-tuning instructions to achieve correctness. + +## Our approach: Who we build for and the principles that guide us + +As the premier multi-platform UI framework from Google, Flutter is uniquely positioned to focus on the AI builders in our ecosystem. The principles ensure that Dart and Flutter remain the most reliable choice for developers building apps that run on any screen. + +### Who we are solving for + +Our strategy addresses builders across the spectrum of AI adoption from traditional to agentic development: + +* **The traditional developer:** Earn trust in AI for those who depend on core Dart and Flutter tooling. Traditional developers care deeply about thinking through problems themselves and using developer tools to dig under the hood. We aim to improve the existing foundation for them, which in turn improves the quality of AI and builds trust in agentic development. + +* **The AI-assisted developer:** Improve productivity with AI for those who have adopted AI coding agents in their Dart and Flutter workflows. These engineers rely on AI for repetitive, tedious tasks and boilerplate code so they can spend more time on complex functional and architectural code. Our developer experience team is exploring AI tooling such as MCP servers, Agent Skills and other solutions in this space. + +* **The AI-first developer :** Make AI work for those who haven’t yet discovered Flutter is the best way to build their apps. These builders are creating apps with natural language. By recognizing where their needs overlap other personas, we ensure our framework is ready for the future of AI development. Collaborations here might include guidance on Flutter with Antigravity, and other vibe-coding products that reflect [“Vibe once, deploy everywhere” concept introduced at Google I/O 2025](https://youtu.be/v6Rzo5khNE8?si=9sKcyc0Rlgevoe7L&t=2432). + +### Our driving principles + +* **Humans first:** Dart remains a “human first” language. We prioritize making code readable and manageable for developers, even when it is generated by agents. + +* **Add, don’t replace:** AI tools should add and improve the experience of building with Dart and Flutter. Each developer should be empowered to use AI assistance as much as they desire, and on a timeline that they choose. We intend to ensure AI is grounded in our documentation and foundational tooling as the source of truth. + +* **Open standards & agent agnostic:** We meet developers where they are. By leveraging open standards like the Model Context Protocol (MCP), we ensure Flutter works well with any agent you choose, not just Gemini. For example, we recently added an MCP tool to ensure hot reload can be used during AI Assisted development. + +* **Trust through quality:** We aim to solve the “verification tax” by ensuring AI-generated code is accurate, idiomatic, and compliant with project standards. Ongoing investments and partnerships with Google Deepmind and Antigravity on model evaluation drive quality from the model through to AI developer tools. If you’re a model engineer, hit us up, we’d love to make your model write beautiful Dart code! + +## Let us know what you think + +We are intentionally experimenting in public because no one knows exactly where this space is going. We invite the community to join us, share your feedback, and help steer the future of Dart and Flutter. Comment here, on social media, or via community forums; we value your thoughts and these conversations. + +… Or continue building apps without AI, that’s totally valid too, and we welcome feedback on our developer roadmap in our previous post. For a deeper dive into our plans, you can view the full [official 2026 Roadmap](https://github.com/flutter/flutter/blob/master/docs/roadmap/Roadmap.md). \ No newline at end of file diff --git a/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0ApRsUADr-G3HYMzY.webp b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0ApRsUADr-G3HYMzY.webp new file mode 100644 index 0000000000..7d4b7a0fb2 Binary files /dev/null and b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0ApRsUADr-G3HYMzY.webp differ diff --git a/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0BrVLb8vUnOxCenlR.webp b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0BrVLb8vUnOxCenlR.webp new file mode 100644 index 0000000000..0afc217e04 Binary files /dev/null and b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0BrVLb8vUnOxCenlR.webp differ diff --git a/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0DOvyVLAckVSQqoax.webp b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0DOvyVLAckVSQqoax.webp new file mode 100644 index 0000000000..1294ead4c7 Binary files /dev/null and b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0DOvyVLAckVSQqoax.webp differ diff --git a/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0DwbyxOv8yHSp1aFb.webp b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0DwbyxOv8yHSp1aFb.webp new file mode 100644 index 0000000000..de2af0a367 Binary files /dev/null and b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0DwbyxOv8yHSp1aFb.webp differ diff --git a/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0FSZ6UZM2gdxag6tr.webp b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0FSZ6UZM2gdxag6tr.webp new file mode 100644 index 0000000000..c34941c8e8 Binary files /dev/null and b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0FSZ6UZM2gdxag6tr.webp differ diff --git a/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0JXYimUKJxblL6LYA.webp b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0JXYimUKJxblL6LYA.webp new file mode 100644 index 0000000000..0e63feda90 Binary files /dev/null and b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0JXYimUKJxblL6LYA.webp differ diff --git a/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0KD5yX1QHIghjtf78.webp b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0KD5yX1QHIghjtf78.webp new file mode 100644 index 0000000000..91a2508506 Binary files /dev/null and b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0KD5yX1QHIghjtf78.webp differ diff --git a/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0dv6mhmjDb4HP59LA.webp b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0dv6mhmjDb4HP59LA.webp new file mode 100644 index 0000000000..934fec4697 Binary files /dev/null and b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0dv6mhmjDb4HP59LA.webp differ diff --git a/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0ecO_ptzWhoeP60fp.webp b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0ecO_ptzWhoeP60fp.webp new file mode 100644 index 0000000000..093a53996d Binary files /dev/null and b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0ecO_ptzWhoeP60fp.webp differ diff --git a/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0vMiJl709u8ABpUE3.webp b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0vMiJl709u8ABpUE3.webp new file mode 100644 index 0000000000..a1cba6653c Binary files /dev/null and b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0vMiJl709u8ABpUE3.webp differ diff --git a/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0wuezwMGAnC91NBTJ.webp b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0wuezwMGAnC91NBTJ.webp new file mode 100644 index 0000000000..eca8a53ea7 Binary files /dev/null and b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/images/0wuezwMGAnC91NBTJ.webp differ diff --git a/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/index.md b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/index.md new file mode 100644 index 0000000000..a77bda7427 --- /dev/null +++ b/sites/www/content/blog/how-flutter-facilitates-collaboration-between-designers-and-developers-at-l-r/index.md @@ -0,0 +1,112 @@ +--- +title: "How Flutter facilitates collaboration between designers and developers at L+R" +description: "You might be familiar with Global Citizen (GC), an organization dedicated to ending world poverty and helping the planet. When GC wanted to…" +publishDate: 2024-02-19 +author: ivan +image: images/0dv6mhmjDb4HP59LA.webp +category: spotlight +layout: blog +--- + +You might be familiar with [Global Citizen](https://www.globalcitizen.org/en/) (GC), an organization dedicated to ending world poverty and helping the planet. When GC wanted to rewrite a mobile app to help with this effort, they reached out to our studio, [L+R](https://levinriegner.com/home), to collaborate on designing, building, and launching the app simultaneously on Android and iOS. + +Flutter’s flexibility, pre-built widget catalog, and robust animation capabilities allowed the L+R team to implement a design-led development process. When using Flutter, our developers have a blank canvas to bring custom designs to life. For L+R, this means our design teams can unleash their creativity to create user-centric apps that look and feel great. + +Here are a few screenshots from the Global Citizen app: + +<DashImage figure src="images/0wuezwMGAnC91NBTJ.webp" /> + + +<DashImage figure src="images/0dv6mhmjDb4HP59LA.webp" /> + + +<DashImage figure src="images/0KD5yX1QHIghjtf78.webp" /> + + +**The design process** + +When starting a new client project, the designers first create a design system in Figma. This consists of brand guidelines — like color palettes and typography — and small reusable components. + +When starting on the Global Citizen app rewrite, the design team expanded upon the existing guidelines to provide a fresh look with higher-contrast elements. Not only does this help to direct the user’s attention but it also improves accessibility. + +<DashImage figure src="images/0DwbyxOv8yHSp1aFb.webp" /> + + +UI elements are created as *components* in Figma. Each component may have different *variants* depending on component type or state. For example, the following diagram shows several button variants: + +<DashImage figure src="images/0JXYimUKJxblL6LYA.webp" /> + + +When developing new components, the design team referenced the Material and iOS design kits in Figma for inspiration. This helps with translating the design to code as these Figma components closely correspond to widgets available in the Material and Cupertino libraries. + +Throughout the design process, the team composed the small, reusable components to form larger feature-specific components that were then combined into entire pages. This empowered Global Citizen’s product team to expand the app with new functionality while remaining true and consistent with the original vision. + +**Project template** + +At L+R, our team has been working with Flutter since its first public release. To make building Flutter apps fast and easy for our team, we’ve created an [open-source repository](https://github.com/levin-riegner/flutter-template) that serves as a project template and provides the cornerstone for all Flutter apps that we build. + +This barebones project contains a set of widgets that can be customized and used in our client’s applications. For example, the buttons represented in the Figma design file (shown above) can be built using the `_BaseButton` class from the project template. This class accepts different colors, text styles, and padding to best match the design system set for that project. + +<DashImage figure src="images/0ApRsUADr-G3HYMzY.webp" /> + + +, + +To make it easy to style these components based on the brand guidelines, we use the `ThemeData` class. Developers take the color schemes and text styles from Figma and map them to the corresponding theme properties. + +<DashImage figure src="images/0DOvyVLAckVSQqoax.webp" /> + + +Our team leveraged the existing properties in `ThemeData` as much as possible, and then used the `ThemeExtension` functionality to complete the remaining configurations. + +**Flutter’s widget library** + +As mentioned above, the project template contains a set of reusable widgets. When developing these, we try to use pre-existing widgets from the Material and Cupertino libraries as much as possible. In the Global Citizen app, about half of the components descend from Material or Cupertino widgets. + +For example, the `TextField` and `TextFormField` widgets from Material contained all the necessary customizations and functionality needed to match the designs. The `TextButton` from Material was also used to implement the `_BaseButton` widget. In this case, we took advantage of `TextButton`’s existing highlight functionality, but we wrapped it into a new widget with extended functionality to better suit our needs. + +<DashImage figure src="images/0BrVLb8vUnOxCenlR.webp" /> + + +Having these out-of-the-box UI components that can easily be styled makes it easier to build and maintain our widgets. However, sometimes our designers create custom components that can’t be recreated using Flutter’s Material or Cupertino widgets. In these cases, the development team creates our own custom widgets from low-level Flutter widgets. + +One example from the Global Citizen app was progress indicators. For this app, our designers created a custom progress indicator as shown in the following GIF. The existing Material and Cupertino progress indicators didn’t work for this design. However, it was easy for our developers to create an entirely new widget using only containers, rows, and columns with intrinsic animations. + +<DashImage figure src="images/0FSZ6UZM2gdxag6tr.webp" /> + + +<DashImage figure src="images/0ecO_ptzWhoeP60fp.webp" /> + + +Community libraries were also incredibly useful for more complex and flexible components. For example we used a community- built [`flushbar`](https://pub.dev/packages/another_flushbar) to display alert notifications inside the Global Citizen app. + +<DashImage figure src="images/0vMiJl709u8ABpUE3.webp" /> + + +**Hot reload** + +When translating the Figma design to Flutter code, it’s important for designers and developers to collaborate. Designers often have feedback that results in small tweaks to styles, layouts, or animations. Hot reload made this collaboration much more efficient. + +With hot reload, developers can tweak the code while screen-sharing with the design team (either during the UX phase, UI concept phase, front-end implementation stage, or even the design-QA stage). While screen sharing, the designer can instantly see the results of changes. This makes debugging and iteration much faster. + +**Smooth animations** + +One thing that helps an app feel polished is adding thoughtful animations. Flutter’s animation framework is flexible and powerful, however it can be difficult for designers to understand. One thing that helped our team was to reference beautiful animations in open source apps. Specifically, we looked at the codebase for the [Wonderous ap](http://wonderous.app)p. We’ve since leveraged the open-source library, [`flutter_animate`](https://pub.dev/packages/flutter_animate), that powers Wonderous to add simple yet enriching animations to key functionalities. + +The video in the following link highlights part of the “Take Action” flow, where the user navigates through a series of informative screens to learn more about the issue. + +Check out the [action_learn_animations](https://drive.google.com/file/d/1OmILf7hZZHbnWbaC6BN-EopYDmQtEOmp/view) video. + +Using flutter_animate, we were able to do the following: + +* Animate the step indicator as the user moves forward + +* Add a slight fade transition between pages + +* Transform the **Play Video** button into a success checkmark after viewing the video + +Overall, Flutter allows a flexible approach to design and development. Flutter’s pre-built widgets make it easier to create reusable UI components that can be styled to match a customer’s branding. The Flutter community plays an important role in how the technology matures, offering high quality libraries and expanding the available resources. Key Flutter features like hot reload supports better collaboration between designers and developers. All of these put together result in an app that is both beautiful and functional! + +For more information about the app, check out [The New Global Citizen App: Daily Activism & Measurable Impact in Your Pocket](https://www.globalcitizen.org/en/content/new-global-citizen-app-impact-activism-every-day/). You can also [download the Global Citizen app](https://www.globalcitizen.org/en/app/download/) to your mobile device. + +[L+R](https://levinriegner.com/home) is an international strategy, design, and mobile technology studio. We offer many services like UX design, mobile development and strategy consulting. \ No newline at end of file diff --git a/sites/www/content/blog/how-ibm-is-creating-a-flutter-center-of-excellence/images/1YqawQ_2If_xPlvIbC0mj3g.webp b/sites/www/content/blog/how-ibm-is-creating-a-flutter-center-of-excellence/images/1YqawQ_2If_xPlvIbC0mj3g.webp new file mode 100644 index 0000000000..19815702f8 Binary files /dev/null and b/sites/www/content/blog/how-ibm-is-creating-a-flutter-center-of-excellence/images/1YqawQ_2If_xPlvIbC0mj3g.webp differ diff --git a/sites/www/content/blog/how-ibm-is-creating-a-flutter-center-of-excellence/index.md b/sites/www/content/blog/how-ibm-is-creating-a-flutter-center-of-excellence/index.md new file mode 100644 index 0000000000..d23b791ea4 --- /dev/null +++ b/sites/www/content/blog/how-ibm-is-creating-a-flutter-center-of-excellence/index.md @@ -0,0 +1,78 @@ +--- +title: "How IBM is creating a Flutter Center of Excellence" +description: "Learn why and how IBM makes their clients successful with Flutter" +publishDate: 2023-09-13 +author: jjmutter +image: images/1YqawQ_2If_xPlvIbC0mj3g.webp +category: spotlight +layout: blog +--- + +The basics of a good client engagement include two critical factors: wise use of resources and timely delivery. + +In today’s mobile and digital-first world, clients have exponentially increased their expectations for experiences, speed, personalization, and convenience. Using Flutter, developers and designers can create beautiful cross-platform web and mobile applications faster, and more efficiently. + +## **Why we’re expanding our Flutter practice** + +### *Flutter helps us deliver client projects faster* + +By targeting multiple platforms from a single codebase, our team can prototype, refine and deploy client apps faster than ever. With Flutter, we can deliver projects three times as fast — without sacrificing quality. + +During the early days of the Covid-19 crisis, the government of Bizkaia, Spain approached IBM to help keep its citizens informed of the latest actions and news. They needed a mobile app — in two weeks. With Flutter, the first version of the app was published in just 11 days. It quickly became the third most-downloaded app in the region. + +<DashImage figure src="images/1YqawQ_2If_xPlvIbC0mj3g.webp" /> + +> “In such a constrained timeframe, Flutter came to our rescue. It was the quickest hard decision we took and one of the most relevant we made. Building the app both for iOS and Android was like having super powers for our development team and beating the race against the clock.” +— IBM Project Director Joanra Mallart + +### *Flutter helps our clients get more for less* + +Delivering great user experiences is always at the forefront of our mind. However, many clients have tight budgets, which often requires foregoing platforms or features that would help their apps succeed. With Flutter, we’re able to exceed client expectations by building without compromise. + +### *Customers are asking about Flutter* + +Flutter has grown in popularity — as seen in the recent [StackOverflow report](https://stackoverflow.blog/2022/02/21/why-flutter-is-the-most-popular-cross-platform-mobile-sdk/) that lists it as the most popular UI framework. As such, we’ve seen a sharp increase in customers coming to us asking how they can use Flutter in their applications. Developers and business stakeholders alike are interested in making use of Flutter’s benefits. + +### *Seamless collaboration with Google* + +As a premier Google Cloud partner, IBM and Google already have a close relationship. By adopting Flutter, we’re able to take advantage of seamless integration between developer offerings while leveraging support from the Google team where necessary. + +## **How we’re building a center of excellence** + +For decades, IBM has been at the forefront of user-centric mobile innovation, led by IBM iX, the experience and design partner within IBM Consulting. We’ve been delivering exceptional mobile experiences to clients across the globe in our 60 IBM iX studios with over 15,000 development, design, and strategy practitioners. + +However, with a large, distributed team, it was vital to create a single place for knowledge sharing. Creating a center of excellence helps our team recommend best practices, reuse learnings and celebrate customer wins in a consolidated community. + +Here’s what we prioritized for creating an IBM Consulting center of excellence for Flutter development and design: + +### **1. Host a regular community forum** + +At IBM, we host a quarterly Flutter community call. On this call, developers and designers can share learnings from client engagements, discuss new Flutter and Dart features, and get updates from the broader Flutter community. + +### **2. Create an environment for asking questions** + +We’ve created a dedicated chat room where developers and designers can ask Flutter-specific questions at any time. We have a group of Flutter experts who jump in to help troubleshoot issues or recommend specific approaches. + +### **3. Centralize knowledge sharing and reusable components** + +Many client projects share similarities in their architecture. Therefore, we’ve created simple project templates that practitioners can pull from to expedite their development. + +### **4. Build educational materials for clients** + +While many clients know about Flutter’s benefits, some might want to better understand how Flutter can help *them*. Building off the[ Flutter pitch deck,](https://docs.google.com/presentation/d/1TblnxXMVO1c75ggdLnA4cPh_nsDVvT--RsFqUIN6Fvg/copy) we’ve developed materials to educate clients on how Flutter differs from other frameworks. We’ve created a central repository of client case studies to easily showcase how Flutter has helped past clients. + +### **5. Create a learning path for Flutter practitioners** + +At IBM, practitioners can become certified in specific technologies or areas. We’ve created a dedicated learning path to help developers learn Flutter and apply those learnings to new client engagements. When a practitioner completes this path, they’re recorded as a Flutter practitioner in our database — so it’s easy to quickly staff Flutter engagements. + +By creating this center of excellence, we’ve helped our practitioners expedite their Flutter learning. With this information, we can deliver client projects better and faster. + +## **The Future: Fueling Excellence in Flutter** + +To date, IBM has seen major success in delivering Flutter projects. Our clients include global banks, federal and state governments, and international military personnel. We have also delivered numerous proofs of concept around the world. + +With Flutter’s agility and IBM Consulting’s legacy of innovation, we’re on a path to shape outstanding mobile experiences that resonate with businesses and their customers. As we embrace this new phase of collaboration, we invite you to join us on this transformative journey. + +Reach out today and discover how [IBM Consulting](https://www.ibm.com/consulting/ibmix) can empower your business with the speed, efficiency, and excellence of Flutter. + +*John Mutter is the Global CTO Experience Engineering at IBM and an IBM Distinguished Engineer.* \ No newline at end of file diff --git a/sites/www/content/blog/how-its-made-holobooth/images/18zOCQg2Qwh_dzC4ndMzoqg.webp b/sites/www/content/blog/how-its-made-holobooth/images/18zOCQg2Qwh_dzC4ndMzoqg.webp new file mode 100644 index 0000000000..1c2c98ac4c Binary files /dev/null and b/sites/www/content/blog/how-its-made-holobooth/images/18zOCQg2Qwh_dzC4ndMzoqg.webp differ diff --git a/sites/www/content/blog/how-its-made-holobooth/images/1YV7A0DsrEyf2r3hvJ6HqNA.gif b/sites/www/content/blog/how-its-made-holobooth/images/1YV7A0DsrEyf2r3hvJ6HqNA.gif new file mode 100644 index 0000000000..983a71797a Binary files /dev/null and b/sites/www/content/blog/how-its-made-holobooth/images/1YV7A0DsrEyf2r3hvJ6HqNA.gif differ diff --git a/sites/www/content/blog/how-its-made-holobooth/images/1_Pp7DaS5DOfQJtMfjJ3m2Q.webp b/sites/www/content/blog/how-its-made-holobooth/images/1_Pp7DaS5DOfQJtMfjJ3m2Q.webp new file mode 100644 index 0000000000..c0261e2196 Binary files /dev/null and b/sites/www/content/blog/how-its-made-holobooth/images/1_Pp7DaS5DOfQJtMfjJ3m2Q.webp differ diff --git a/sites/www/content/blog/how-its-made-holobooth/images/1gqq0z-7kyXE0GS086zbVUw.webp b/sites/www/content/blog/how-its-made-holobooth/images/1gqq0z-7kyXE0GS086zbVUw.webp new file mode 100644 index 0000000000..3d558c329f Binary files /dev/null and b/sites/www/content/blog/how-its-made-holobooth/images/1gqq0z-7kyXE0GS086zbVUw.webp differ diff --git a/sites/www/content/blog/how-its-made-holobooth/images/1hdTGZAcinWlt2yR-4CLt2w.gif b/sites/www/content/blog/how-its-made-holobooth/images/1hdTGZAcinWlt2yR-4CLt2w.gif new file mode 100644 index 0000000000..b49be60a86 Binary files /dev/null and b/sites/www/content/blog/how-its-made-holobooth/images/1hdTGZAcinWlt2yR-4CLt2w.gif differ diff --git a/sites/www/content/blog/how-its-made-holobooth/images/1mvB6J_LUq2WQWJpIZ4A7IQ.gif b/sites/www/content/blog/how-its-made-holobooth/images/1mvB6J_LUq2WQWJpIZ4A7IQ.gif new file mode 100644 index 0000000000..0582d849ee Binary files /dev/null and b/sites/www/content/blog/how-its-made-holobooth/images/1mvB6J_LUq2WQWJpIZ4A7IQ.gif differ diff --git a/sites/www/content/blog/how-its-made-holobooth/index.md b/sites/www/content/blog/how-its-made-holobooth/index.md new file mode 100644 index 0000000000..9781f100e1 --- /dev/null +++ b/sites/www/content/blog/how-its-made-holobooth/index.md @@ -0,0 +1,117 @@ +--- +title: "How it’s made: Holobooth" +description: "A virtual photo booth experience showcasing Flutter and Machine Learning" +publishDate: 2023-01-24 +author: verygoodopensource +image: images/18zOCQg2Qwh_dzC4ndMzoqg.webp +category: spotlight +layout: blog +--- + +Introducing the Flutter Forward [Holobooth](https://holobooth.flutter.dev/), a new experience showcasing the power of [Flutter](https://flutter.dev/), [Firebase](https://firebase.google.com/), and Machine Learning (using [MediaPipe](https://mediapipe.dev/) and [TensorFlow.js](https://tensorflow.org/js)) in a virtual photo booth experience. Start by selecting your avatar (Dash or Sparky) and transport yourself to a tropical beach, volcanic mountain, outer space, the ocean floor, or somewhere else! Since we can’t transport everyone to Nairobi to attend [Flutter Forward](https://flutter.dev/events/flutter-forward) in person, we wanted to provide a virtual experience that is just as exciting! With Holobooth, you can capture a short video to commemorate your virtual visit. Then, show your friends by sharing on Twitter or Facebook. + +<DashImage figure src="images/18zOCQg2Qwh_dzC4ndMzoqg.webp" alt="Try out the Flutter Forward Holobooth at [holobooth.flutter.dev](https://holobooth.flutter.dev/)" caption="Try out the Flutter Forward Holobooth at [holobooth.flutter.dev](https://holobooth.flutter.dev/)" /> + + +The Holobooth builds on the first version of the [Photo Booth app from Google I/O 2021](https://medium.com/flutter/how-its-made-i-o-photo-booth-3b8355d35883). Instead of taking photos of you and Dash or Sparky, Holobooth uses machine learning to control animations of Dash or Sparky using your facial expressions. + +We’ll break down how our team collaborated with Google to create a more immersive and futuristic photo booth experience by tapping into the power of Google tools. We used [Flutter](https://flutter.dev/) and [Firebase](https://firebase.google.com/) to build the Holobooth app. Web ML in JavaScript allowed us to take the experience to the next level with virtual, interactive, 3D avatars. Let’s dive into how we built it! + +## Detecting faces with TensorFlow.js + +One of the most exciting features of the [Holobooth](https://holobooth.flutter.dev/) is the ability to map live video of your face onto a 3D model of Dash (or Sparky) as you travel through their virtual world. If your face expresses surprise, Dash’s face expresses surprise, and so on. To achieve this, we used the [camera](https://pub.dev/packages/camera) plugin for Flutter web and [TensorFlow.js](https://www.tensorflow.org/js) to detect the user’s face within the frame of the camera. More specifically, we used the [MediaPipe FaceMesh](https://github.com/tensorflow/tfjs-models/tree/master/face-landmarks-detection) model, which estimates 468 3D face landmarks in real-time, to detect features of the user’s face within the camera frame across web and mobile browsers. + +<DashImage figure src="images/1_Pp7DaS5DOfQJtMfjJ3m2Q.webp" alt="Features detected with the MediaPipe FaceMesh model" caption="Features detected with the MediaPipe FaceMesh model" /> + + +Based on the position of each facial feature, we can determine if the user is in frame, if their eyes or mouth are open, and more. As the user moves around the camera view, the MediaPipe [FaceMesh](https://github.com/tensorflow/tfjs-models/tree/master/face-landmarks-detection) model (available via the [TensorFlow.js Face Landmarks Detection package](https://github.com/tensorflow/tfjs-models/tree/master/face-landmarks-detection)) ensures that we can track the exact coordinates of the user’s features so that we can mirror them on Dash or Sparky. For more details, you can dig into the [face_geometry.dart file](https://github.com/flutter/holobooth/blob/1c5bc00f0b43dd56ea7f964b26756a5991d6502e/packages/face_geometry/lib/src/face_geometry.dart). While there isn’t an official Dart package for TensorFlow.js yet, the Dart [JS](https://pub.dev/packages/js) package allowed us to import the javascript library into a Flutter web app (see the [tensorflow_models](https://github.com/flutter/holobooth/tree/1c5bc00f0b43dd56ea7f964b26756a5991d6502e/packages/tensorflow_models) package folder for more details). + +```javascript + FaceGeometry({ + required tf.Face face, + required tf.Size size, + }) : this._( + rotation: FaceRotation(keypoints: face.keypoints), + leftEye: LeftEyeGeometry( + keypoints: face.keypoints, + boundingBox: face.boundingBox, + ), + rightEye: RightEyeGeometry( + keypoints: face.keypoints, + boundingBox: face.boundingBox, + ), + mouth: MouthGeometry( + keypoints: face.keypoints, + boundingBox: face.boundingBox, + ), + distance: FaceDistance( + boundingBox: face.boundingBox, + imageSize: size, + ), + ); + + const FaceGeometry._({ + required this.rotation, + required this.mouth, + required this.leftEye, + required this.rightEye, + required this.distance, + }); +``` + + +## Animating backgrounds and avatars with Rive and TensorFlow.js + +We turned to [Rive](https://rive.app/) to bring Holobooth animations to life. Rive is a web app built in Flutter that provides tools for building highly performant, lightweight, interactive animations that are easy to integrate into a Flutter app. We collaborated with talented designers at Rive and [HOPR design studio](https://www.hopr.tv/) to create animated Rive graphics that work seamlessly within our Flutter app. The animated backgrounds and avatars are Rive animations. + +<DashImage figure src="images/1mvB6J_LUq2WQWJpIZ4A7IQ.gif" alt="Move your face to see the Rive model mimic your behavior" caption="Move your face to see the Rive model mimic your behavior" /> + + +The avatars use Rive [State Machines](https://help.rive.app/editor/state-machine) that allow us to control how an avatar behaves and looks. In the Rive State Machine, designers specify all of the inputs. Inputs are values that are controlled by your app. You can think of them as the contract between design and engineering teams. Your product’s code can change the values of the inputs at any time, and the State Machine reacts to those changes. + +For Holobooth, we used inputs to control things like how wide a mouth is open or closed. Using the feature detection from the [FaceMesh](https://github.com/tensorflow/tfjs-models/tree/master/face-landmarks-detection) model, we can map them to the corresponding coordinates on our avatar models. Using the StateMachineController, we transform the input from the models to determine how the avatar appears on screen. + +```javascript +class CharacterStateMachineController extends StateMachineController { + CharacterStateMachineController(Artboard artboard) + : super( + artboard.animations.whereType<StateMachine>().firstWhere( + (stateMachine) => stateMachine.name == 'State Machine 1', + ), +``` + + +For example, the avatar models have a property to measure the [openness of the mouth](https://github.com/flutter/holobooth/blob/1c5bc00f0b43dd56ea7f964b26756a5991d6502e/lib/rive/widgets/character_animation.dart) (measured from 0–1 where 0 is fully closed and 1 is fully open). If the user closes their mouth within the camera view, the app provides the corresponding value and feeds it into the avatar model so you see your avatar’s mouth also closes on the screen. + +## Capturing the dynamic photo with Firebase + +The main feature of Holobooth is the GIF or video that you can share to celebrate Flutter Forward. We turned to [Cloud Functions for Firebase](https://firebase.google.com/products/functions) to help us generate and upload your dynamic photo to [Cloud Storage for Firebase](https://firebase.google.com/products/storage). Once you press the camera button, the app starts capturing frames for a duration of 5 seconds. With [ffmpeg](https://www.npmjs.com/package/fluent-ffmpeg), we use a Cloud Function to convert the frames into a single GIF and video that are then uploaded to [Cloud Storage for Firebase](https://firebase.google.com/products/storage). You can choose to download your GIF or video for later viewing or to manually upload it to social media. + +<DashImage figure src="images/1YV7A0DsrEyf2r3hvJ6HqNA.gif" alt="Capturing the dynamic photo" caption="Capturing the dynamic photo" /> + + +To share your GIF directly to Twitter or Facebook, you can click the share button. You are then taken to the selected platform with a pre-populated post containing a photo of the first frame of your video. To see the full video, click on the link to your holocard — a web page that displays your video in full and a button directing visitors to try out Holobooth for themselves! + +<DashImage figure src="images/1gqq0z-7kyXE0GS086zbVUw.webp" alt="Example holocard" caption="Example holocard" /> + + +## Challenges and how we addressed them + +Holobooth contains a lot of elements that are expanding what’s possible with Flutter — like using machine learning models and Rive graphics, all while ensuring a performant, smooth experience for users. + +Working with TensorFlow.js was a first for us at Very Good Ventures. There are currently no official Flutter libraries, so much of our early work on this project focused on experimenting with the available models to figure out which one fit our needs. Once we settled on the landmark detection model, we then had to make sense of the data that the models output and map them onto the Rive animations. Here is an early exploration with face detection: + +<DashImage figure src="images/1hdTGZAcinWlt2yR-4CLt2w.gif" alt="Early exploration of face detection" caption="Early exploration of face detection" /> + + +The official Flutter [camera](https://pub.dev/packages/camera) plugin gave us a lot of functionality out of the box, but it currently doesn’t support streaming images on the web. For Holobooth, we [forked](https://github.com/flutter/plugins/compare/main...VGVentures:plugins:feat/camera-video-controller?expand=1) the camera plugin to add this functionality. We hope that this is supported by the [official plugin in the future](https://github.com/flutter/flutter/issues/92460). + +Another challenge was optimizing for performance. Recording the screen can be an expensive operation because the app is capturing lots of data. We also had to take into account that users would be accessing this app from different browsers and devices. We wanted to ensure that the app is performant and provides a smooth experience for users no matter what device they’re using. When accessing Holobooth on desktop, video backgrounds are animated and reflect a landscape orientation. To optimize for mobile browsers, backgrounds are static and cropped to fit portrait mode orientation. Since a mobile screen is smaller than on desktop, we also optimized the resolution of image assets to reduce the initial page load and the amount of data used by the device. + +For more details on how we addressed these challenges and more, you can check out the [open source code](https://github.com/flutter/holobooth). We hope that this can serve as inspiration for developers wanting to experiment with TensorFlow.js, Rive, and videos, or even those just looking to optimize their web apps. + +## Looking forward + +In creating this demo, we wanted to explore the potential for Flutter web apps to integrate with TensorFlow.js models in an easy, performant, and fun way. While a lot of what we’ve built is still experimental, we’re excited for the future of machine learning in Flutter apps to create delightful experiences for users on any device! Join the community conversation, let us know what you think, and how you might use machine learning in your next Flutter project. + +[**Take a video in the Holobooth and share it with us on social media!**](https://holobooth.flutter.dev/) \ No newline at end of file diff --git a/sites/www/content/blog/how-its-made-i-o-flip/images/1-8FtGtAwiERpc5_ctA0GKg.webp b/sites/www/content/blog/how-its-made-i-o-flip/images/1-8FtGtAwiERpc5_ctA0GKg.webp new file mode 100644 index 0000000000..21ade7be84 Binary files /dev/null and b/sites/www/content/blog/how-its-made-i-o-flip/images/1-8FtGtAwiERpc5_ctA0GKg.webp differ diff --git a/sites/www/content/blog/how-its-made-i-o-flip/images/1nGcHw9zmkDT1oGhYwuZMqA.jpeg b/sites/www/content/blog/how-its-made-i-o-flip/images/1nGcHw9zmkDT1oGhYwuZMqA.jpeg new file mode 100644 index 0000000000..ae8531ca87 Binary files /dev/null and b/sites/www/content/blog/how-its-made-i-o-flip/images/1nGcHw9zmkDT1oGhYwuZMqA.jpeg differ diff --git a/sites/www/content/blog/how-its-made-i-o-flip/images/1oMUDkIUdJaEb6tX1jdQjKg.gif b/sites/www/content/blog/how-its-made-i-o-flip/images/1oMUDkIUdJaEb6tX1jdQjKg.gif new file mode 100644 index 0000000000..cf5a8fda48 Binary files /dev/null and b/sites/www/content/blog/how-its-made-i-o-flip/images/1oMUDkIUdJaEb6tX1jdQjKg.gif differ diff --git a/sites/www/content/blog/how-its-made-i-o-flip/index.md b/sites/www/content/blog/how-its-made-i-o-flip/index.md new file mode 100644 index 0000000000..127b734da2 --- /dev/null +++ b/sites/www/content/blog/how-its-made-i-o-flip/index.md @@ -0,0 +1,166 @@ +--- +title: "How It’s Made: I/O FLIP" +description: "AI-generated text and images add some fun to this card game built with Flutter, Firebase, and Google Cloud" +publishDate: 2023-05-18 +author: verygoodopensource +image: images/1nGcHw9zmkDT1oGhYwuZMqA.jpeg +category: events +layout: blog +--- + +<DashImage figure src="images/1nGcHw9zmkDT1oGhYwuZMqA.jpeg" /> + + +## How It’s Made: I/O FLIP + +### AI-generated text and images add some fun to this card game built with Flutter, Firebase, and Google Cloud + +[I/O FLIP](http://flip.withgoogle.com) is an AI-designed card game powered by Google and built in Flutter. It unites a handful of Google products and techniques. This includes [Flutter](https://flutter.dev/), [Firebase](https://firebase.google.com/), generative AI tools, Dreambooth on Muse, [PaLM API](https://developers.googleblog.com/2023/03/announcing-palm-api-and-makersuite.html), and [MakerSuite](https://developers.googleblog.com/2023/03/announcing-palm-api-and-makersuite.html). Players can play multiple matches and share cards from their game to social media. + +To play the game, visit [http://flip.withgoogle.com](http://flip.withgoogle.com). To start and to generate your deck, answer a few prompts. To make your hand and join a match, select three cards. You’ll use these cards to play your opponent for three rounds in which the highest card wins the round. The fun doesn’t have to end when the match does. Keep playing matches to increase your win streak and try to make the leaderboard! + +Let’s dig into the technical details of we used Flutter and Firebase to build I/O FLIP. + +## A card game created with Flutter + +The I/O FLIP user interface, animations, hologram effects, and backend, were all built with Flutter and Dart. + +To start, we leveraged the [Flutter Casual Games Toolkit](https://docs.flutter.dev/resources/games-toolkit). We used the out-of-the-box audio functionality, game lifecycle, and app navigation with [go_router](https://pub.dev/packages/go_router) as a foundation. From there, we built out the game logic and UI. I/O FLIP is a [responsive web app](https://docs.flutter.dev/ui/layout/adaptive-responsive). It resizes its UI based on the players’ screen. The app also handles input based on the device the player uses. It receives touch input when accessed on a mobile device or tablet and mouse input on desktop. + +Playing cards are a key element of I/O FLIP. Thousands of cards could end up in a player’s deck. Each card displays a Google mascot, element, and power value. The elements come into play when they go up against each other. For example, when someone plays a fire card and their opponent plays a water card, the water card receives a penalty of 10 points. We also used records, a [new feature of Dart 3](https://medium.com/dartlang/dart-3-alpha-f1458fb9d232), to render a frame based on the card element. + +```dart +… + (String, SvgPicture) _mapSuitNameToAssets() { + switch (suitName) { + case 'fire': + if (isRare) { + return ( + Assets.images.cardFrames.holos.cardFire.keyName, + Assets.images.suits.card.fire.svg(), + ); + } + return ( + Assets.images.cardFrames.cardFire.keyName, + Assets.images.suits.card.fire.svg(), + ); +… +``` + + +### Cards created with AI-generated images and descriptions + +<DashImage figure src="images/1-8FtGtAwiERpc5_ctA0GKg.webp" alt="Example I/O FLIP cards" caption="Example I/O FLIP cards" /> + + +Each card in I/O FLIP is unique because it contains an AI-generated image and description. At the beginning of the game, players answer two prompts. These prompts help populate a deck of 12 cards that feature images and descriptions that AI models pre-generated. + +Google team used two technologies to pre-generate the images: [Muse](https://muse-model.github.io/), a Transformer text-to-image model, and [DreamBooth](https://dreambooth.github.io/). DreamBooth enables synthesizing a subject in diverse scenes, poses, views, and lighting conditions. Each card contains one of four Google mascots: [Dash](https://docs.flutter.dev/dash), Sparky, [Android](https://source.android.com/docs/setup/about/brands#robot-android), or Dino, and a location. The mascot also has an item that designates their team. The “team type” prompt at the beginning of the game seed the creation of this item. For example, selecting “wizard” may result in a character with a wizard hat, magic wand, or something else wizardly! + +The Google team used the [PaLM API](https://developers.googleblog.com/2023/03/announcing-palm-api-and-makersuite.html) to pre-generate card descriptions. The PaLM API accesses Google’s large language models. The prompts at the beginning of the game included the team type and power type for their team. Let’s say you selected team “Wizard” and power “Magnetism”. When your cards generate, one card’s description includes context to the generated image including the character’s special powers. For example, “Dash the Wizard lives in a castle with his pet dragon. He loves to cast spells and make people laugh.” + +Flutter composes the cards via the `GameCard` widget. This widget receives the card’s data: name, description, image, and power. Once it creates the card, it applies a border to depict the card’s suit element. If a card is a special card, Flutter applies a foil shader effect. + +To learn more about how the Generative AI aspects of the game were created, check out [this Google Developer blog post](https://www.google.com/url?q=https://developers.googleblog.com/2023/05/how-its-made-io-flip-adds-twist-to.html&sa=D&source=docs&ust=1684366112610573&usg=AOvVaw1fj9CV5BxCPvxUEGfcZF39). + +### Shaders add foil effect to special cards + +<DashImage figure src="images/1oMUDkIUdJaEb6tX1jdQjKg.gif" alt="I/O FLIP uses a fragment shader to render the holo effect on a card" caption="I/O FLIP uses a fragment shader to render the holo effect on a card" /> + + +Flutter supports [fragment shaders](https://docs.flutter.dev/ui/advanced/shaders). To generate these per-pixel visual effects, Flutter runs the OpenGL Shading Language (GLSL) on the device’s GPU. Trading card collectors might remember the feeling of opening a pack to find a special edition card with a shiny, holographic foil. I/O FLIP includes special foil cards, too. They are valued at 100 points. Regular cards have a point values ranging from 10 to 99. We used custom shaders to render the foil effect. + +We implemented the foil shader in the `foil.frag` file. The effect uses the following constants: + +* `STRENGTH`. This mixes the original pixel color with the color of the foil effect. This ranges from 0.0 for no effect to 1.0 for full effect. + +* `SATURATION`. This sets the intensity of a color. This ranges from 0.0 for grayscale, or absence of color, to 1.0 for full color or absence of black. + +* `LIGHTNESS`. This ranges from 0.0 for full black to 1.0 for full white. + +The shader also receives input through [uniforms](https://docs.flutter.dev/ui/advanced/shaders#uniforms), in this case `resolution` and `offset`. The uniform `sampler2d` called `tSource` represents the card image to which the shader is applied. The final result is a card with a foil effect. + +```dart +vec4 rainbowEffect(vec2 uv) { + vec4 srcColor = texture(tSource, uv); + float hue = uv.x / (1.75 + abs(offset.x)) + offset.x / 3.0; + float lightness = LIGHTNESS + 0.25 * (0.5 + offset.y * (0.5 - uv.y)); + hue = fract(hue); + + float c = (1.0 - abs(2.0 * lightness - 1.0)) * SATURATION; + float x = c * (1.0 - abs(mod(hue / (1.0 / 6.0), 2.0) - 1.0)); + float m = LIGHTNESS - c / 2.0; + + vec3 rainbowPrime; + + if (hue < 1.0 / 6.0) { + rainbowPrime = vec3(c, x, 0.0); + } else if (hue < 1.0 / 3.0) { + rainbowPrime = vec3(x, c, 0.0); + } else if (hue < 0.5) { + rainbowPrime = vec3(0.0, c, x); + } else if (hue < 2.0 / 3.0) { + rainbowPrime = vec3(0.0, x, c); + } else if (hue < 5.0 / 6.0) { + rainbowPrime = vec3(x, 0.0, c); + } else { + rainbowPrime = vec3(c, 0.0, x); + } + + vec3 rainbow = rainbowPrime + m; + return mix(srcColor, vec4(rainbow, srcColor.a), STRENGTH); +} +``` + + +## Firebase enables game hosting and sharing to social media + +All of the gameplay communication happens in real time via [Firebase’s Cloud Firestore](https://firebase.google.com/docs/firestore). Firebase Storage hosts the card assets that generate players’ card decks. We also use Cloud Firestore to track the “Highest Win Streak” on the leaderboard. When a leaderboard adds new leader, the [`firedart`](https://pub.dev/packages/firedart) package adds it to Firestore. + +### Dart Frog enables sharing code between the backend and frontend + +I/O FLIP needed a backend to prevent cheating. This server-authoritative game logic prevent malicious clients from sending fabricated requests. Dart Frog keeps the game logic, such as the winner of each round, on the backend. It also shares this code between the Flutter frontend and the Firestore backend. Sharing code has a few benefits. It allows us to share logic. For example, if one player wins a round, the game can display a win animation without needing to query Firebase every time. Sharing code also sped development, since the team could write backend and frontend code in the same language: Dart. We deployed the I/O FLIP Dart Frog server to [Cloud Run](https://cloud.google.com/run). This means the server code runs in Google Cloud and can autoscale, so the app can handle many players at once. + +```dart +FutureOr<Response> onRequest(RequestContext context) async { + if (context.request.method == HttpMethod.post) { + final cardsRepository = context.read<CardsRepository>(); + final promptRepository = context.read<PromptRepository>(); + + final body = await context.request.json() as Map<String, dynamic>; + final prompt = Prompt.fromJson(body); + + if (!await promptRepository.isValidPrompt(prompt)) { + return Response(statusCode: HttpStatus.badRequest); + } + + final characterClass = prompt.characterClass; + if (characterClass == null) { + return Response(statusCode: HttpStatus.badRequest); + } + + final characterPower = prompt.power; + if (characterPower == null) { + return Response(statusCode: HttpStatus.badRequest); + } + + final cards = await cardsRepository.generateCards( + characterClass: characterClass, + characterPower: characterPower, + ); + return Response.json( + body: {'cards': cards.map((e) => e.toJson()).toList()}, + ); + } + return Response(statusCode: HttpStatus.methodNotAllowed); +} +``` + + +Dart Frog also facilitates sharing on social media. When selecting cards, a player can share an individual card to Twitter or Facebook. When you submit your score to the leaderboard, you can share your hand to Twitter or Facebook. Once the user clicks **Share**, Dart Frog generates a pre-populated post. This post contains text and a link to a webpage with your corresponding hand or card and a button for visitors to play I/O FLIP themselves! + +## What’s next + +I/O FLIP showcases how to combine Flutter and Firebase, along with Google generative AI tools and techniques, in a fun game that people around the world can play. + +[Play a game](http://flip.withgoogle.com) and show us your hand, or dig into the [open source code](https://github.com/flutter/io_flip)! \ No newline at end of file diff --git a/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0CN8nNM1HaOjg9SfQ.webp b/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0CN8nNM1HaOjg9SfQ.webp new file mode 100644 index 0000000000..49b1fc2bda Binary files /dev/null and b/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0CN8nNM1HaOjg9SfQ.webp differ diff --git a/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0MVI3wAXUfJdGls5X.webp b/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0MVI3wAXUfJdGls5X.webp new file mode 100644 index 0000000000..51b0e30daa Binary files /dev/null and b/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0MVI3wAXUfJdGls5X.webp differ diff --git a/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0OQnK58irOAv-Pjzq.webp b/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0OQnK58irOAv-Pjzq.webp new file mode 100644 index 0000000000..95647ebf88 Binary files /dev/null and b/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0OQnK58irOAv-Pjzq.webp differ diff --git a/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0Zd9s-7LFN9u17Ouo.webp b/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0Zd9s-7LFN9u17Ouo.webp new file mode 100644 index 0000000000..e57a5c4c1d Binary files /dev/null and b/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0Zd9s-7LFN9u17Ouo.webp differ diff --git a/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0diM5YKjX2b2OgNvD.webp b/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0diM5YKjX2b2OgNvD.webp new file mode 100644 index 0000000000..ef832caf26 Binary files /dev/null and b/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0diM5YKjX2b2OgNvD.webp differ diff --git a/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0kkxXNd0m-t4sjCAo.webp b/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0kkxXNd0m-t4sjCAo.webp new file mode 100644 index 0000000000..87e2b71131 Binary files /dev/null and b/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0kkxXNd0m-t4sjCAo.webp differ diff --git a/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0tXpB_n44hmjGxHXf.webp b/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0tXpB_n44hmjGxHXf.webp new file mode 100644 index 0000000000..7b6a807563 Binary files /dev/null and b/sites/www/content/blog/how-its-made-i-o-photo-booth/images/0tXpB_n44hmjGxHXf.webp differ diff --git a/sites/www/content/blog/how-its-made-i-o-photo-booth/index.md b/sites/www/content/blog/how-its-made-i-o-photo-booth/index.md new file mode 100644 index 0000000000..0bb537d06a --- /dev/null +++ b/sites/www/content/blog/how-its-made-i-o-photo-booth/index.md @@ -0,0 +1,231 @@ +--- +title: "How It’s Made: I/O Photo Booth" +description: "A closer look at building a web app with Flutter and Firebase" +publishDate: 2021-05-17 +author: verygoodopensource +image: images/0diM5YKjX2b2OgNvD.webp +category: events +layout: blog +--- + +<DashImage figure src="images/0diM5YKjX2b2OgNvD.webp" /> + + +We (the folks at Very Good Ventures) teamed up with Google to bring an interactive experience to this year’s Google I/O: a [photo booth](https://photobooth.flutter.dev/)! You can take pictures with well-known Google mascots: [Flutter’s Dash](https://flutter.dev/dash), Android Jetpack, Chrome’s Dino, and Firebase’s Sparky, and decorate photos with stickers, including party hats, pizza, funky glasses, and more. Finally, you can share photos on social media and download them to update your profile picture for the event! + +<DashImage figure src="images/0OQnK58irOAv-Pjzq.webp" alt="Flutter’s Dash, Firebase’s Sparky, Android Jetpack, and Chrome’s Dino" caption="Flutter’s Dash, Firebase’s Sparky, Android Jetpack, and Chrome’s Dino" /> + + +We built the I/O Photo Booth using [Flutter on the web](https://flutter.dev/web) and [Firebase](https://firebase.google.com/). Because [Flutter now offers support for web apps](https://medium.com/flutter/whats-new-in-flutter-2-0-fe8e95ecc65), we thought it would be a great way to make this app easily accessible to attendees all over the world for this year’s virtual Google I/O. Flutter’s web support eliminates the barrier of having to install an app from an app store and also gives you the flexibility to run it on your device of choice: mobile, desktop, or tablet. That opens up the I/O Photo Booth experience to anyone with access to any browser and device without requiring a download. + +Even though I/O Photo Booth was designed to be a web experience, all of the code is written with a platform-agnostic architecture. When native support for elements like the camera plugin are available for their respective platforms, the same code works across all platforms (desktop, web, and mobile). + +## Making a virtual photo booth with Flutter + +### Building a Flutter camera plugin for the web + +The first challenge came with building a camera plugin for Flutter on the web. Initially, we reached out to the team at [Baseflow](https://www.baseflow.com/), because they maintain the existing open source [Flutter camera plugin](https://github.com/Baseflow/flutter-plugins). While Baseflow works on building top-notch camera plugin support for iOS and Android, we were happy to work in parallel on web support for the plugin using the [federated plugin approach](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#federated-plugins). We stuck as closely as possible to the official plugin interface so that we could merge it back into the official plugin when it was ready. + +We identified two APIs that would be critical for building the I/O Photo Booth camera experience in Flutter. + +1. **Initializing the camera:** The app first needs access to your device camera. On desktop, this is likely the webcam, and on mobile, we chose the front-facing camera. We also provide a desired resolution of 1080p to maximize the camera quality based on your device. + +1. **Taking the photo:** We used the built-in [`HtmlElementView`](https://api.flutter.dev/flutter/widgets/HtmlElementView-class.html) that uses platform views to render native web elements as Flutter widgets. In this project, we render a [`VideoElement`](https://api.flutter.dev/flutter/dart-html/VideoElement-class.html) as a native HTML element, which is what you see on the screen before you take your photo. We use a [`CanvasElement`](https://api.flutter.dev/flutter/dart-html/CanvasElement-class.html) that is rendered as another HTML element. This allows us to capture the image from the media stream when you click the take photo button. + +``` +Future<CameraImage> takePicture() async { + final videoWidth = videoElement.videoWidth; + final videoHeight = videoElement.videoHeight; + final canvas = html.CanvasElement( + width: videoWidth, + height: videoHeight, + ); + canvas.context2D + ..translate(videoWidth, 0) + ..scale(-1, 1) + ..drawImageScaled(videoElement, 0, 0, videoWidth, videoHeight); + final blob = await canvas.toBlob(); + return CameraImage( + data: html.Url.createObjectUrl(blob), + width: videoWidth, + height: videoHeight, + ); +} +``` + + +### Camera permissions + +After we got the Flutter Camera plugin working on the web, we created an abstraction to display different UIs depending on the camera permissions. For example, while waiting for you to allow or deny browser camera use, or if there are no available cameras to access, we can display an instructional message. + +``` +Camera( + controller: _controller, + placeholder: (_) => const SizedBox(), + preview: (context, preview) => PhotoboothPreview( + preview: preview, + onSnapPressed: _onSnapPressed, + ), + error: (context, error) => PhotoboothError(error: error), +) +``` + + +In this abstraction, the placeholder returns the initial UI as the app waits for you to grant permission to the camera. Preview returns the UI after you grant permission and provides the real-time video stream of the camera. The error builder allows us to capture an error if it occurs and renders a corresponding error message. + +### Mirroring the photo + +Our next challenge was mirroring the photo. If we took the photo using the camera as is, what you’d see isn’t what you’re used to seeing when looking in the mirror. [Some devices have a setting to handle exactly this](https://9to5mac.com/2020/07/09/iphone-mirror-selfie-photos/), so that if you take a photo with the front-facing camera, you’ll see the mirrored version when capturing the photo. + +In our first approach, we tried capturing the default camera view, and then applying a 180-degree transform around the y-axis. This appeared to work, but then we ran into [an issue](https://github.com/flutter/flutter/issues/79519) where Flutter would occasionally override the transform, causing the video to revert to the un-mirrored version. + +With the help of the Flutter team, we addressed this issue by wrapping the `VideoElement` in a [`DivElement`](https://api.flutter.dev/flutter/dart-html/DivElement-class.html) and updating the `VideoElement` to fill the `DivElement`’s width and height. This allowed us to apply the mirror to the video element without Flutter overriding the transform effect, because the parent element is a `div`. This approach gave us the desired mirrored camera view! + +<DashImage figure src="images/0Zd9s-7LFN9u17Ouo.webp" alt="Un-mirrored view" caption="Un-mirrored view" /> + + +<DashImage figure src="images/0kkxXNd0m-t4sjCAo.webp" alt="Mirrored view" caption="Mirrored view" /> + + +### Sticking to a strict aspect ratio + +Enforcing a strict aspect ratio of 4:3 for large screens and 3:4 for small screens is harder than it seems! It was important to enforce this ratio both to adhere to the overall design for the web app as well as to ensure that the photo looks pixel perfect when you share it on social media. This was a challenging task, because the aspect ratio of the built-in camera on devices varies widely. + +To enforce a strict aspect ratio, the app first requests the maximum resolution possible from the device camera using the JavaScript [`getUserMedia` API](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia). We then feed this API into the `VideoElement` stream, which is what you see in the camera view (mirrored, of course). We also applied an [`object-fit`](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property to ensure that the video element covers its parent container. This sets the aspect ratio using the built-in `AspectRatio` widget from Flutter. As a result, the camera doesn’t make any assumptions about the aspect ratio being displayed; it always returns the maximum resolution supported, and then conforms to the constraints provided by Flutter (in this case 4:3 or 3:4). + +``` +final orientation = MediaQuery.of(context).orientation; +final aspectRatio = orientation == Orientation.portrait + ? PhotoboothAspectRatio.portrait + : PhotoboothAspectRatio.landscape; +return Scaffold( + body: _PhotoboothBackground( + aspectRatio: aspectRatio, + child: Camera( + controller: _controller, + placeholder: (_) => const SizedBox(), + preview: (context, preview) => PhotoboothPreview( + preview: preview, + onSnapPressed: () => _onSnapPressed( + aspectRatio: aspectRatio, + ), + ), + error: (context, error) => PhotoboothError(error: error), + ), + ), +); +``` + + +### Adding friends and stickers with drag and drop + +A huge part of the I/O Photo Booth experience is taking a photo with your favorite Google friends and adding props. You are able to drag and drop the friends and props within the photo, as well as resize and rotate them until you get an image that you like. You’ll notice that, when adding a friend to the screen, you can drag and resize them. The friends are also animated — sprite sheets to achieve this effect. + +``` +for (final character in state.characters) + DraggableResizable( + canTransform: character.id == state.selectedAssetId, + onUpdate: (update) { + context.read<PhotoboothBloc>().add( + PhotoCharacterDragged( + character: character, + update: update, + ), + ); + }, + child: _AnimatedCharacter(name: character.asset.name), + ), +``` + + +To resize the objects, we created a draggable, resizable widget that can be wrapped around any Flutter widget, in this case, the friends and props. This widget uses a [`LayoutBuilder`](https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html) to handle scaling the widgets based on the constraints of the viewport. Internally, we used [`GestureDetectors`](https://api.flutter.dev/flutter/widgets/GestureDetector-class.html) to hook into `onScaleStart`, `onScaleUpdate`, and `onScaleEnd`. These callbacks provide details about the gesture needed to reflect the changes you make to the friends and props. + +The[`Transform`](https://api.flutter.dev/flutter/widgets/Transform-class.html) widget and 4D Matrix transformations handle scaling and rotating the friends and props based on the various gestures that you made, as reported by multiple `GestureDetector`s. + +``` +Transform( + alignment: Alignment.center, + transform: Matrix4.identity() + ..scale(scale) + ..rotateZ(angle), + child: _DraggablePoint(...), +) +``` + + +Finally, we created a separate package to determine whether your device supports touch input. The draggable, resizable widget adapts, based on touch capabilities. On devices with touch input, resizable anchors and a rotation icon aren’t visible, because you can pinch and pan to manipulate the image directly, whereas on devices without touch input (such as your desktop device), the anchors and rotation icon are added to accommodate clicking and dragging. + +<DashImage figure src="images/0MVI3wAXUfJdGls5X.webp" /> + + +## Prioritizing Flutter on the web + +### Web-first development with Flutter + +This was one of the first web-only projects that we’ve built with Flutter, and it has different characteristics to a mobile app. + +We needed to ensure that the app was both [responsive and adaptive](https://flutter.dev/docs/development/ui/layout/adaptive-responsive) for any browser on any device. That is, we had to make sure that I/O Photo Booth would scale according to browser size and be able to handle both mobile and web inputs. We did this in a few ways: + +* **Responsive resize:** You should be able to resize your browser to a desired size, and the UI should respond accordingly. If your browser window is in a portrait orientation, the camera flips from a landscape view with the 4:3 aspect ratio to a portrait view with a 3:4 aspect ratio. + +* **Responsive design:** The design for desktop browsers displays Dash, the Android Jetpack, Dino, and Sparky on the right, and for mobile, they appear at the top. The desktop design also uses a drawer on the right side of the camera, and mobile uses the BottomSheet class. + +* **Adaptive input:** If you access the I/O Photo Booth from a desktop, then mouse clicks are considered inputs, and if you are on a tablet or phone, touch input is used. This is especially important when it comes to resizing stickers and placing them within the photo. Mobile devices support pinching and panning, and desktop supports click and drag. + +### Scalable architecture + +We also used our approach to building scalable mobile apps for this application. We started I/O Photo Booth with a strong foundation, including sound null safety, internationalization, and 100% unit and widget test coverage from the first commit. We used [flutter_bloc](https://pub.dev/packages/flutter_bloc) for state management, because it allowed for easily testing business logic and observes all state changes in the app. This is particularly useful for developer logs and traceability, because we could see exactly what changed from state to state and isolate issues more quickly. + +We also implemented a feature-driven monorepo structure. For example, stickers, share, and the live camera preview are implemented in their own folders, where each folder contains its respective UI components and business logic. These integrate with external dependencies, such as the camera plugin, which live within the packages subdirectory. This architecture allowed our team to work on multiple features in parallel without disrupting the work of others, minimized merge conflicts, and enabled us to reuse code effectively. For example, the UI component library is a separate package called [`photobooth_ui`](https://github.com/flutter/photobooth/tree/main/packages/photobooth_ui), and the camera plugin is separate as well. + +By separating the components into independent packages, we can extract and open source the individual components that aren’t tied to this specific project. Even the UI component library package can be open sourced for the Flutter community, similar to the [Material](https://flutter.dev/docs/development/ui/widgets/material) and [Cupertino](https://flutter.dev/docs/development/ui/widgets/cupertino) component libraries. + +## Firebase + Flutter = A perfect match + +### Firebase Auth, storage, hosting, and more + +Photo Booth leverages the Firebase ecosystem for various backend integrations. The [`firebase_auth` package](https://pub.dev/packages/firebase_auth) supports anonymously signing the user in as soon as the app launches. Each session uses Firebase Auth to create an anonymous user with a unique ID. + +This comes into play when you arrive at the share page. You can either download your photo to save as your profile picture, or you can share directly to social media. If you download the photo, it’s stored locally on your device. If you share the photo, we store the photo in Firebase using the [`firebase_storage` package](https://pub.dev/packages/firebase_storage) so that we can retrieve it later, to populate the social post. + +We defined [Firebase Security Rules](https://firebase.google.com/docs/rules) on the Firebase storage bucket to make photos immutable after creation. This prevents other users from modifying or deleting photos in the storage bucket. In addition, we use [Object Lifecycle Management](https://cloud.google.com/storage/docs/lifecycle) provided by Google Cloud to define a rule that deletes all objects that are 30 days old, but you can request to have your photos deleted sooner by following the instructions outlined in the app. + +This application also uses [Firebase Hosting](https://firebase.google.com/docs/hosting) for fast and secure hosting of the web app. The [action-hosting-deploy](https://github.com/FirebaseExtended/action-hosting-deploy) GitHub Action allowed us to automate deployments to Firebase Hosting based on the target branch. When we merge changes into the main branch, the action triggers a workflow that builds and deploys the development flavor of the application to Firebase Hosting. Similarly, when we merge changes into the release branch, the action triggers a production deployment. The combination of the GitHub Action with Firebase Hosting allowed our team to iterate quickly and always have a preview of the latest build. + +Finally, we used [Firebase Performance Monitoring](https://firebase.google.com/products/performance) to monitor key web performance metrics. + +### Getting social with Cloud Functions + +Before generating your social post, we first make sure that the photo looks pixel perfect. The final image includes a nice frame to commemorate the I/O Photo Booth and is cropped to the 4:3 or 3:4 aspect ratio so that it looks great on the social post. + +We use the [`OffscreenCanvas`](https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas) API or a [`CanvasElement`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas) as a polyfill to composite the original photo, along with the layers, that contain your friends and props, and generate a single image that you can download. The [`image_compositor` package](https://github.com/flutter/photobooth/tree/main/packages/image_compositor) handles this processing step. + +We then tap into Firebase’s powerful [Cloud Functions](https://firebase.google.com/docs/functions) to assist with sharing the photo to social media. When you click the share button, you are taken to a new tab on the selected platform with a pre-populated post. The post has a URL that redirects to the cloud function that we wrote. When the browser analyzes the URL, it detects the dynamic meta information that the cloud function generated. This information allows the browser to display a nice preview image of your photo in your social post and a link to a share page where your followers can view the photo and navigate back to the I/O Photo Booth app to take their own. + +``` +function renderSharePage(imageFileName: string, baseUrl: string): string { + const context = Object.assign({}, BaseHTMLContext, { + appUrl: baseUrl, + shareUrl: `${baseUrl}/share/${imageFileName}`, + shareImageUrl: bucketPathForFile(`${UPLOAD_PATH}/${imageFileName}`), + }); + return renderTemplate(shareTmpl, context); +} +``` + + +The final product looks something like this: + +<DashImage figure src="images/0tXpB_n44hmjGxHXf.webp" /> + + +For more information about how to use Firebase in your Flutter projects, check out this [codelab](https://firebase.google.com/codelabs/firebase-get-to-know-flutter#0). + +## Final product + +This project was a good example of a web-first approach to building apps. We were pleasantly surprised by how similar our workflow for building this web application was, compared to our experience building mobile applications with Flutter. We had to consider elements like viewport sizes, responsiveness, touch versus mouse input, image load times, browser compatibility, and all the other considerations that come with building for the web. However, we were still writing Flutter code using the same patterns, architecture, and coding standards. We felt at home while building for the web. The tooling and growing ecosystem of Flutter packages, including the Firebase suite of tools, made I/O Photo Booth possible. + +<DashImage figure src="images/0CN8nNM1HaOjg9SfQ.webp" alt="Very Good Ventures team who worked on I/O Photo Booth" caption="Very Good Ventures team who worked on I/O Photo Booth" /> + + +We’ve open sourced all the code. Check out the [photo_booth](https://github.com/flutter/photobooth) project on GitHub and show us your photos on Facebook and Twitter using #IOPhotoBooth! \ No newline at end of file diff --git a/sites/www/content/blog/how-to-build-your-own-flutter-slide-puzzle/images/04sa1RI3GzrlpRcQ4.webp b/sites/www/content/blog/how-to-build-your-own-flutter-slide-puzzle/images/04sa1RI3GzrlpRcQ4.webp new file mode 100644 index 0000000000..14641f79bc Binary files /dev/null and b/sites/www/content/blog/how-to-build-your-own-flutter-slide-puzzle/images/04sa1RI3GzrlpRcQ4.webp differ diff --git a/sites/www/content/blog/how-to-build-your-own-flutter-slide-puzzle/images/0BHUg3jHaaIRFW_5O.webp b/sites/www/content/blog/how-to-build-your-own-flutter-slide-puzzle/images/0BHUg3jHaaIRFW_5O.webp new file mode 100644 index 0000000000..abb3234d07 Binary files /dev/null and b/sites/www/content/blog/how-to-build-your-own-flutter-slide-puzzle/images/0BHUg3jHaaIRFW_5O.webp differ diff --git a/sites/www/content/blog/how-to-build-your-own-flutter-slide-puzzle/images/0YPUf0ml0ggq7RJIL.webp b/sites/www/content/blog/how-to-build-your-own-flutter-slide-puzzle/images/0YPUf0ml0ggq7RJIL.webp new file mode 100644 index 0000000000..e29c15654e Binary files /dev/null and b/sites/www/content/blog/how-to-build-your-own-flutter-slide-puzzle/images/0YPUf0ml0ggq7RJIL.webp differ diff --git a/sites/www/content/blog/how-to-build-your-own-flutter-slide-puzzle/images/1r5DaUW3Cdoad6GQBwWIh3Q.webp b/sites/www/content/blog/how-to-build-your-own-flutter-slide-puzzle/images/1r5DaUW3Cdoad6GQBwWIh3Q.webp new file mode 100644 index 0000000000..d4b6325dbb Binary files /dev/null and b/sites/www/content/blog/how-to-build-your-own-flutter-slide-puzzle/images/1r5DaUW3Cdoad6GQBwWIh3Q.webp differ diff --git a/sites/www/content/blog/how-to-build-your-own-flutter-slide-puzzle/index.md b/sites/www/content/blog/how-to-build-your-own-flutter-slide-puzzle/index.md new file mode 100644 index 0000000000..c399c3818d --- /dev/null +++ b/sites/www/content/blog/how-to-build-your-own-flutter-slide-puzzle/index.md @@ -0,0 +1,152 @@ +--- +title: "How to build your own Flutter slide puzzle" +description: "By now you’ve probably read through the Flutter Puzzle Hack challenge and are already thinking of how to push your creativity to new…" +publishDate: 2022-01-20 +author: verygoodopensource +image: images/1r5DaUW3Cdoad6GQBwWIh3Q.webp +category: spotlight +layout: blog +--- + +<DashImage figure src="images/1r5DaUW3Cdoad6GQBwWIh3Q.webp" /> + + +By now you’ve probably read through the [Flutter Puzzle Hack](https://flutterhack.devpost.com/) challenge and are already thinking of how to push your creativity to new limits. We’re here to give you some more info about how the sample codebase is structured and give you some ideas for creating your contest submission. + +## Slide puzzle architecture + +The provided source code implements a layered architecture with `flutter_bloc` ****to manage the state. Blocs handle everything from the game logic to theming. All state is managed in a consistent way; for any updates to the logic of the puzzle, you’ll only need to find the corresponding bloc and update it. The reset and shuffle game buttons, timer, and countdown are all separate blocs, so these might be other places where you consider implementing something creative: maybe an hourglass for a timer? Or a snazzy shuffle sequence? + +Note that all of the game logic is contained within a single bloc called `PuzzleBloc`. This will update with events such as tapping a tile to move the puzzle tile with `TileTapped` event or resetting the puzzle board completely with `PuzzleReset`. Each time a puzzle is updated, a new puzzle state is emitted. + +``` +class PuzzleBloc extends Bloc<PuzzleEvent, PuzzleState> { + PuzzleBloc(this._size, {this.random}) : super(const PuzzleState() { + on<PuzzleInitialized>(_onPuzzleInitialized); + on<TileTapped>(_onTileTapped); + on<PuzzleReset>(_onPuzzleReset); + } + + void _onPuzzleInitialized( + PuzzleInitialized event, + Emitter<PuzzleState> emit, + ) {...} + void _onTileTapped( + TileTapped event, + Emitter<PuzzleState> emit, + ) {...} + void _onPuzzleReset( + PuzzleReset event, + Emitter<PuzzleState> emit, + ) {...} +} +``` + + +## Customizing the theme + +The sample puzzle code contains both a Simple and Dashatar theme. These can be used as a jumping off point for your own customizations — borrow as much as you need, or start from scratch. How you implement your puzzle is up to you! One place you may consider getting creative is the puzzle theming. + +<DashImage figure src="images/0BHUg3jHaaIRFW_5O.webp" /> + + +All of the theming in the demo happens at the top of the `PuzzlePage`. It is abstracted, so you’ll only need to update the theme elements in one place and have the changes reflected everywhere. Both the Simple and Dashatar themes define a range of options such as screen background, menu, logo, buttons, text colors, whether the theme displays a timer (present in the Dashatar theme, but not in the Simple theme), and more. You can find these in the “dashatar” and “simple” directories at the root of the repository. + +``` +/// {@template simple_theme} +/// The simple puzzle theme. +/// {@endtemplate} +class SimpleTheme extends PuzzleTheme { + /// {@macro simple_theme} + const SimpleTheme() : super(); + + @override + Color get backgroundColor => PuzzleColors.white; + + @override + Color get buttonColor => PuzzleColors.primary6; + + @override + Color get hoverColor => PuzzleColors.primary3; + + @override + Color get pressedColor => PuzzleColors.primary7; + + ... +} +``` + + +Each puzzle theme has a `LayoutDelegate` that computes the layout of this theme. You can create new themes by reusing the same layout objects and only adjusting some theme options. For more advanced designs, the whole theme `LayoutDelegate` can be customized. For example, you can override the `backgroundBuilder` to make a custom background, displayed only on a large screen. + +``` +@override +Widget backgroundBuilder(PuzzleState state) { + return Positioned( + bottom: 74, + right: 50, + child: ResponsiveLayoutBuilder( + small: (_, child) => const SizedBox(), + medium: (_, child) => const SizedBox(), + large: (_, child) => const DashatarThemePicker(), + ), + ); +} +``` + + +## Implementing animations + +Animations are a great element to explore in your slide puzzle. While the Simple theme does not contain any animations, the Dashatar code implements a few animations, some of which are [staggered](https://docs.flutter.dev/development/ui/animations/staggered-animations). These animations are driven by a single animation controller adjusted with [`Interval`](https://api.flutter.dev/flutter/animation/Interval-class.html) to delay the animation and [`Tween`](https://api.flutter.dev/flutter/animation/Tween-class.html) to define the range of values of the animation. This can be seen in the success state of the Dashatar puzzle where multiple widgets appear sequentially by animating their offset and opacity. Similarly, each tick of the countdown timer slowly grows in and then fades out just as the next tick is about to appear using the same technique. + +<DashImage figure src="images/0YPUf0ml0ggq7RJIL.webp" /> + + +Most of the animations in the Dashatar theme are implicit. This means that there is no need to write the whole animation yourself, as widgets animate changes to properties on their own. As an example, take a look at the `DashatarPuzzleTile` widget for how to animate the movement of tiles when the user taps a tile. Whenever a tile is tapped, its current position is updated and transitioned over the given `movementDuration` thanks to implicitly animated `AnimatedAlign`. + +``` +class DashatarPuzzleTile extends StatelessWidget { + ... + + final Tile tile; + + @override + Widget build(BuildContext context) { + return AnimatedAlign( + alignment: FractionalOffset( + (tile.currentPosition.x - 1) / (size - 1), + (tile.currentPosition.y - 1) / (size - 1), + ), + duration: movementDuration, + curve: Curves.easeInOut, + child: ResponsiveLayoutBuilder(...), + ); + } +} +``` + + +## Designing for the web + +The sample puzzle was built for the web. It implements responsive design for three screen sizes: small, medium, and large. In addition, there is a `ResponsiveLayoutBuilder` that uses a wrapper around the Flutter `LayoutBuilder` widget, which allows you to specify different widgets based on the different breakpoints. + +Since we targeted the web, there are some additional considerations to keep in mind. The app pre-caches images and some audio to help optimize performance during the game. When a user arrives at the default Simple puzzle game, the assets for the Dashatar version load in the background (we took a similar approach with loading all of the props for [I/O Photo Booth](https://github.com/flutter/photobooth)). This will make sure that once the user navigates to the Dashatar theme, most assets will already be loaded, ensuring a smooth user experience. + +While the app is at home on the web, you could challenge yourself to explore creative ways to make the puzzle extend to multiple platforms. What might your puzzle look like on mobile and desktop? How can you adapt your ideas for multiple platforms? + +## Accessibility + +The slide puzzle was built with accessibility in mind. Users are able to use a keyboard to interact with the puzzle. This is done with `RawKeyboardListener`, a widget that calls a callback whenever the user presses or releases a key on a keyboard. Users can also interact with the app using screen readers. This is done with [semantic labels](https://api.flutter.dev/flutter/widgets/Semantics-class.html). There are also tooltips that appear for some actions using the `Tooltip` widget. If you’re building your slide puzzle submission from scratch, we highly recommend making your puzzle accessible to all users by using similar strategies above. + +## Additional ideas + +Feel free to use the demo codebase as a foundation for your submission. Or, you can implement a new idea from scratch. The important thing is that your submission is a working slide puzzle — but how you get there is up to you! + +The Simple and Dashatar examples should give you some ideas of where you can customize, or cool things you can create on your own. One interesting idea we explored was projecting the [Flutter Plasma created by Felix Blaschke](https://flutterplasma.dev/) onto the puzzle tiles. The plasma effect can be achieved using the `CustomPaint` widget wrapped with a `Transform` widget and animated with an `AnimationController`. You can find an [example of the plasma renderer used in the demos here](https://github.com/felixblaschke/sa3_liquid/blob/main/lib/liquid/plasma/plasma.dart). + +<DashImage figure src="images/04sa1RI3GzrlpRcQ4.webp" /> + + +Another idea would be to fetch images or other data from an API. For example, you could use the [Google Photos API](https://developers.google.com/photos) to make a slide puzzle using photos from one of your favorite Google Photos albums. The sky is the limit with this challenge! + +View the sample puzzle code [here](https://github.com/VGVentures/slide_puzzle). Share your creations with us on Twitter with the #FlutterPuzzleHack. We can’t wait to see what you create! \ No newline at end of file diff --git a/sites/www/content/blog/how-to-choose-which-flutter-animation-widget-is-right-for-you/images/0LJO-BZLYkE68UdlX.webp b/sites/www/content/blog/how-to-choose-which-flutter-animation-widget-is-right-for-you/images/0LJO-BZLYkE68UdlX.webp new file mode 100644 index 0000000000..2b13b2b599 Binary files /dev/null and b/sites/www/content/blog/how-to-choose-which-flutter-animation-widget-is-right-for-you/images/0LJO-BZLYkE68UdlX.webp differ diff --git a/sites/www/content/blog/how-to-choose-which-flutter-animation-widget-is-right-for-you/images/0T-YZa36Dv2a9xTLc.webp b/sites/www/content/blog/how-to-choose-which-flutter-animation-widget-is-right-for-you/images/0T-YZa36Dv2a9xTLc.webp new file mode 100644 index 0000000000..5de4a0c162 Binary files /dev/null and b/sites/www/content/blog/how-to-choose-which-flutter-animation-widget-is-right-for-you/images/0T-YZa36Dv2a9xTLc.webp differ diff --git a/sites/www/content/blog/how-to-choose-which-flutter-animation-widget-is-right-for-you/images/0VMomX3dS34GVis3W.webp b/sites/www/content/blog/how-to-choose-which-flutter-animation-widget-is-right-for-you/images/0VMomX3dS34GVis3W.webp new file mode 100644 index 0000000000..4367b057f9 Binary files /dev/null and b/sites/www/content/blog/how-to-choose-which-flutter-animation-widget-is-right-for-you/images/0VMomX3dS34GVis3W.webp differ diff --git a/sites/www/content/blog/how-to-choose-which-flutter-animation-widget-is-right-for-you/images/0fjdxxt_qXL1o9y_V.webp b/sites/www/content/blog/how-to-choose-which-flutter-animation-widget-is-right-for-you/images/0fjdxxt_qXL1o9y_V.webp new file mode 100644 index 0000000000..8950105f78 Binary files /dev/null and b/sites/www/content/blog/how-to-choose-which-flutter-animation-widget-is-right-for-you/images/0fjdxxt_qXL1o9y_V.webp differ diff --git a/sites/www/content/blog/how-to-choose-which-flutter-animation-widget-is-right-for-you/images/0oiCAPHSQGyFgZlUd.webp b/sites/www/content/blog/how-to-choose-which-flutter-animation-widget-is-right-for-you/images/0oiCAPHSQGyFgZlUd.webp new file mode 100644 index 0000000000..e690f2cbe7 Binary files /dev/null and b/sites/www/content/blog/how-to-choose-which-flutter-animation-widget-is-right-for-you/images/0oiCAPHSQGyFgZlUd.webp differ diff --git a/sites/www/content/blog/how-to-choose-which-flutter-animation-widget-is-right-for-you/index.md b/sites/www/content/blog/how-to-choose-which-flutter-animation-widget-is-right-for-you/index.md new file mode 100644 index 0000000000..6a8de7c78d --- /dev/null +++ b/sites/www/content/blog/how-to-choose-which-flutter-animation-widget-is-right-for-you/index.md @@ -0,0 +1,88 @@ +--- +title: "How to Choose Which Flutter Animation Widget is Right for You?" +description: "There are many widgets for animations, but which one to use?" +publishDate: 2020-04-01 +author: fitzface +image: images/0oiCAPHSQGyFgZlUd.webp +category: tutorial +layout: blog +--- + +*To watch this post in video form, check out [our YouTube video here](https://youtu.be/GXIJJkq_H8g).* + +*This article was originally authored by Emily Fortuna, and has been posted on her behalf.* + +So, you’ve decided to include an animation in your Flutter app — how exciting! The thing is, there are a lot of different animation widgets so figuring out which one is most appropriate can feel overwhelming. Fortunately, this article is here to help! + +I’ll walk through a series of questions you can ask yourself about the animation you have in mind, to help you determine how you should create it. The other thing to remember is that the animation widgets provided in the core Flutter library are pretty low-level. This means that if you have a complex animation in mind, I recommend you check out some of the animation packages available on [pub.dev](https://pub.dev/) that provide higher-level interfaces. + +Take a look at the following decision tree, which I’ll explain in this article: + +<DashImage figure src="images/0T-YZa36Dv2a9xTLc.webp" alt="*A pocket flow chart for all your animation decisions.*" caption="*A pocket flow chart for all your animation decisions.*" /> + + +Broadly speaking, there are two main types of animations that you might want to include in your Flutter app: drawing-based animations, and code-based animations. + +*Code-based animations* are widget-focused and are rooted in standard layout and style primitives like rows, columns, colors, or text styles. That’s not to say they’re boring or simple, but at their heart they tend to enhance a particular existing widget’s appearance or transition rather than act as a standalone widget in their own right. + +*Drawing-based animations*, in contrast, look like someone drew them. They often are stand-alone sprites, like game characters, or involve transformations that would be challenging to express purely in code. + +So the first question to ask yourself is: “Is my animation more like a drawing, or does it look like something that you can build out of Flutter widget primitives?” If your animation is more like a drawing, or you are working with a design team who will provide vector or raster image assets, then I recommend you use a third-party tool such as Rive or Lottie to build your animation graphically, and then export it to Flutter. There are several packages that can help you include these assets in your Flutter app. + +Otherwise, if your animations involve mutating widgets — such as changing colors, shapes, or positions — you’ll get to write some Flutter code! + +## Explicit or implicit? + +Flutter code-based animations come in two flavors: implicit and explicit animations. The next step is figuring out which type you need. + +<DashImage figure src="images/0LJO-BZLYkE68UdlX.webp" alt="*Implicit Animation Widgets animate when the values change.*" caption="*Implicit Animation Widgets animate when the values change.*" /> + + +Implicit animations rely on simply setting a new value for some widget property and Flutter takes care of animating it from the current value to the new value. These widgets are easy to use and are amazingly powerful. All of the animations you see above are done with implicitly animated widgets. Implicit animations are a good place to start when looking to animate something. + +Explicit animations require an AnimationController. They are called “explicit” because they only start animating when explicitly asked to. You can use explicit animations to do everything you can with implicit animations, plus a little more. The annoying thing is you have to manually manage the life-cycle of the AnimationController since it’s not a widget, which means putting it inside a stateful widget. For that reason, your code is generally simpler if you can get away with using an implicit animation widget. + +There are three questions to ask yourself to determine what type of widget you need: Does my animation repeat forever? By “forever” I mean while it’s on a certain screen, or as long as a certain condition is true, such as music playing. + +The second question to ask yourself is whether the values in your animation are discontinuous. An example of what I mean by a discontinuous animation is this growing circle animation. The circle repeatedly grows small-large, small-large. It never grows small-large and then shrinks back down again. In this case, the circle’s size is discontinuous. + +<DashImage figure src="images/0VMomX3dS34GVis3W.webp" alt="*A circle that only grows, never shrinks. It’s a discontinuous animation!*" caption="*A circle that only grows, never shrinks. It’s a discontinuous animation!*" /> + + +The last question to ask yourself is whether multiple widgets are animating in a coordinated fashion together? For example: + +<DashImage figure src="images/0fjdxxt_qXL1o9y_V.webp" alt="*Multiple boxes animating together.*" caption="*Multiple boxes animating together.*" /> + + +If you answered “yes” to any of those three questions, you need to use an explicit widget. Otherwise, you can use an implicit widget! Once you’ve decided whether you need an implicit or explicit widget, the last question will lead you to finding the specific widget you need. + +## Which widget? + +Ask yourself, is there a built-in widget for my needs? If you’re looking for a built-in implicit animation widget, look for widgets named AnimatedFoo where “*Foo*” is the property you want to animate, such as [AnimatedOpacity](https://flutter.dev/docs/cookbook/animation/opacity-animation). Also check [AnimatedContainer](https://api.flutter.dev/flutter/widgets/AnimatedContainer-class.html) as it is an extremely powerful and versatile widget for many different implicit animations. + +If you can’t find the built-in implicit animation you need, you can use [TweenAnimationBuilder](https://api.flutter.dev/flutter/widgets/TweenAnimationBuilder-class.html) to create a custom implicit animation. Conversely, if you’re looking for a built-in explicit widget, they are usually called *Foo*Transition, where “*Foo*” is the property you’re animating, such as [SlideTransition](https://api.flutter.dev/flutter/widgets/SlideTransition-class.html). + +If you can’t find a relevant built-in explicit animation, there is one last question you need to ask yourself: *Do I want my animation to be a standalone widget or part of another surrounding widget?* The answer to this is mostly of a matter of taste. If you want a standalone custom explicit animation, you should extend [AnimatedWidget](https://api.flutter.dev/flutter/widgets/AnimatedWidget-class.html). Otherwise, you can use [AnimatedBuilder](https://api.flutter.dev/flutter/widgets/AnimatedBuilder-class.html). + +There’s one last option to consider if you’re seeing performance problems, and that is animating with CustomPainter. You can use it much like AnimatedWidget, but CustomPainter paints directly to the Canvas, without the standard widget build paradigm. When used well, you can create some neat, extremely custom effects or save on performance. When misused, though, your animation could cause more performance issues. So, take care and, much like manual memory management, make sure you know what you’re doing before sprinkling shared pointers everywhere. + +## Conclusion + +In summary, there are a series of high-level questions you can ask yourself that guide how to create your animation. That sequence of questions creates a decision tree for determining what widget or package is right for your needs. If you collapse those end-points, they fall into a line, approximately indicating difficulty from left to right. Thanks for reading, and go forth and create great Flutter animations — through a third-party framework, or with packages, either explicitly, or implicitly! + +<DashImage figure src="images/0oiCAPHSQGyFgZlUd.webp" alt="*Animation widgets from easiest … to hardest.*" caption="*Animation widgets from easiest … to hardest.*" /> + + +Articles in this series: + +* [How to Choose Which Flutter Animation Widget is Right for You?](https://medium.com/p/79ecfb7e72b5/edit) (this article) + +* [Flutter animation basics with implicit animations](https://medium.com/flutter/flutter-animation-basics-with-implicit-animations-95db481c5916) + +* [Custom Implicit Animations in Flutter…with TweenAnimationBuilder](https://medium.com/flutter/custom-implicit-animations-in-flutter-with-tweenanimationbuilder-c76540b47185) + +* [Directional animations with built-in explicit animations](https://medium.com/flutter/directional-animations-with-built-in-explicit-animations-3e7c5e6fbbd7) + +* [When should I useAnimatedBuilder or AnimatedWidget?](https://medium.com/flutter/when-should-i-useanimatedbuilder-or-animatedwidget-57ecae0959e8) + +* [Animation deep dive](https://medium.com/flutter/animation-deep-dive-39d3ffea111f) \ No newline at end of file diff --git a/sites/www/content/blog/how-to-create-stateless-widgets/images/0GoTVv7PbwxvjCh4S.webp b/sites/www/content/blog/how-to-create-stateless-widgets/images/0GoTVv7PbwxvjCh4S.webp new file mode 100644 index 0000000000..33243619d7 Binary files /dev/null and b/sites/www/content/blog/how-to-create-stateless-widgets/images/0GoTVv7PbwxvjCh4S.webp differ diff --git a/sites/www/content/blog/how-to-create-stateless-widgets/images/0QLq-DId9PheGXmk9.webp b/sites/www/content/blog/how-to-create-stateless-widgets/images/0QLq-DId9PheGXmk9.webp new file mode 100644 index 0000000000..3557489caf Binary files /dev/null and b/sites/www/content/blog/how-to-create-stateless-widgets/images/0QLq-DId9PheGXmk9.webp differ diff --git a/sites/www/content/blog/how-to-create-stateless-widgets/images/10IZb-Rcyw4yBo3CXQM5zTg.webp b/sites/www/content/blog/how-to-create-stateless-widgets/images/10IZb-Rcyw4yBo3CXQM5zTg.webp new file mode 100644 index 0000000000..f24d12dd97 Binary files /dev/null and b/sites/www/content/blog/how-to-create-stateless-widgets/images/10IZb-Rcyw4yBo3CXQM5zTg.webp differ diff --git a/sites/www/content/blog/how-to-create-stateless-widgets/images/17qIL1q-ZP005QS_zTt0j5A.webp b/sites/www/content/blog/how-to-create-stateless-widgets/images/17qIL1q-ZP005QS_zTt0j5A.webp new file mode 100644 index 0000000000..f0aebe8c00 Binary files /dev/null and b/sites/www/content/blog/how-to-create-stateless-widgets/images/17qIL1q-ZP005QS_zTt0j5A.webp differ diff --git a/sites/www/content/blog/how-to-create-stateless-widgets/images/1NW7SMJnwzENu3avGQpDgAg.webp b/sites/www/content/blog/how-to-create-stateless-widgets/images/1NW7SMJnwzENu3avGQpDgAg.webp new file mode 100644 index 0000000000..3c1063d4f8 Binary files /dev/null and b/sites/www/content/blog/how-to-create-stateless-widgets/images/1NW7SMJnwzENu3avGQpDgAg.webp differ diff --git a/sites/www/content/blog/how-to-create-stateless-widgets/images/1O-D8lfgoAmMqHViU2dWuyg.webp b/sites/www/content/blog/how-to-create-stateless-widgets/images/1O-D8lfgoAmMqHViU2dWuyg.webp new file mode 100644 index 0000000000..4ec9543609 Binary files /dev/null and b/sites/www/content/blog/how-to-create-stateless-widgets/images/1O-D8lfgoAmMqHViU2dWuyg.webp differ diff --git a/sites/www/content/blog/how-to-create-stateless-widgets/images/1bk-ErE0V-mSofAW6dze8jQ.webp b/sites/www/content/blog/how-to-create-stateless-widgets/images/1bk-ErE0V-mSofAW6dze8jQ.webp new file mode 100644 index 0000000000..08ce1d473d Binary files /dev/null and b/sites/www/content/blog/how-to-create-stateless-widgets/images/1bk-ErE0V-mSofAW6dze8jQ.webp differ diff --git a/sites/www/content/blog/how-to-create-stateless-widgets/images/1dH1_jmYHx1UxfioPteGiKQ.webp b/sites/www/content/blog/how-to-create-stateless-widgets/images/1dH1_jmYHx1UxfioPteGiKQ.webp new file mode 100644 index 0000000000..d9a2f1b3b7 Binary files /dev/null and b/sites/www/content/blog/how-to-create-stateless-widgets/images/1dH1_jmYHx1UxfioPteGiKQ.webp differ diff --git a/sites/www/content/blog/how-to-create-stateless-widgets/images/1gG5opaiBFJ0A1ka9tZMeSQ.webp b/sites/www/content/blog/how-to-create-stateless-widgets/images/1gG5opaiBFJ0A1ka9tZMeSQ.webp new file mode 100644 index 0000000000..1471685ed9 Binary files /dev/null and b/sites/www/content/blog/how-to-create-stateless-widgets/images/1gG5opaiBFJ0A1ka9tZMeSQ.webp differ diff --git a/sites/www/content/blog/how-to-create-stateless-widgets/images/1lRBiFALMxP8-RPAsmlk7pw.webp b/sites/www/content/blog/how-to-create-stateless-widgets/images/1lRBiFALMxP8-RPAsmlk7pw.webp new file mode 100644 index 0000000000..8f17efa2a4 Binary files /dev/null and b/sites/www/content/blog/how-to-create-stateless-widgets/images/1lRBiFALMxP8-RPAsmlk7pw.webp differ diff --git a/sites/www/content/blog/how-to-create-stateless-widgets/index.md b/sites/www/content/blog/how-to-create-stateless-widgets/index.md new file mode 100644 index 0000000000..e28e25e5a0 --- /dev/null +++ b/sites/www/content/blog/how-to-create-stateless-widgets/index.md @@ -0,0 +1,407 @@ +--- +title: "How to Create Stateless Widgets" +description: "When getting started with Flutter, one of the first things you need to learn about is stateless widgets. We recorded a series that covers…" +publishDate: 2019-02-14 +author: aguinis +image: images/0GoTVv7PbwxvjCh4S.webp +category: tutorial +layout: blog +--- + +When getting started with Flutter, one of the first things you need to learn about is stateless widgets. We recorded a series that covers stateless widgets, stateful widgets, inherited widgets, and keys. + +In the following video, I go over what a Flutter widget is, and how to use StatelessWidgets in a Flutter app: + +<YoutubeEmbed id="wE7khGHVkYY" title="How to Create Stateless Widgets - Flutter Widgets 101 Ep. 1" fullwidth="true"/> + + +For those that prefer to read articles over watching videos, this post explains what Flutter widgets are, how they combine to become interfaces, and how to compose a UI with stateless widgets. + +## Part 1 — Making a dog app using stateless widgets + +To keep things quick, I’m starting with a basic app here. It contains a Scaffold widget, an AppBar widget, and a couple Text widgets that display info about my yellow Labrador, Rocky. + +<DashImage figure src="images/0GoTVv7PbwxvjCh4S.webp" /> + + +Widgets are the basic building blocks of a Flutter app. Each one is an immutable declaration of an aspect of the user interface, and they can take on many tasks. + +For example, there are: + +* structural widgets — like a button or menu + +* stylistic widgets that propagate a font or color scheme + +* layout-related widgets — like padding + +* and many more + +You can also compose new widgets from existing widgets, so the combinations are endless. Let me show you what I mean. + +Say I want a color behind my dog’s name. + +I can do that by wrapping the Text widget with a DecoratedBox. + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(new DogApp()); +} + +class DogApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'My Dog App', + home: Scaffold( + appBar: AppBar( + title: Text('Yellow Lab'), + ), + body: Center( + child: DecoratedBox( // here is where I added my DecoratedBox + decoration: BoxDecoration(color: Colors.lightBlueAccent), + child: Text('Rocky'), + ), + ), + ), + ); + } +} + +``` + +And now my text widget has a background color. + +<DashImage figure src="images/1bk-ErE0V-mSofAW6dze8jQ.webp" /> + + +Maybe I’d like padding around the text. + +I can accomplish that by adding a padding widget. I’ll specify 8 logical pixels of padding around Rocky’s name. + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(new DogApp()); +} + +class DogApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'My Dog App', + home: Scaffold( + appBar: AppBar( + title: Text('Yellow Lab'), + ), + body: Center( + child: DecoratedBox( + decoration: BoxDecoration(color: Colors.lightBlueAccent), + child: Padding( + padding: const EdgeInsets.all(8.0) + child: Text('Rocky'), + ), + ), + ), + ), + ); + } +``` + +And now I’ve got padding. + +<DashImage figure src="images/1dH1_jmYHx1UxfioPteGiKQ.webp" /> + + +This process of putting widgets together is what we call “composition.” I’m composing my interface by combining simple widgets, each of which handles one particular job: Padding pads things, DecoratedBox decorates a box, and so on. + +Now, let’s say I go to the animal shelter and meet a couple yellow labs I can’t live without. I can add a Column widget inside the Center widget, and add their names. + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(new DogApp()); +} + +class DogApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'My Dog App', + home: Scaffold( + appBar: AppBar( + title: Text('Yellow Lab'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + DecoratedBox( + decoration: BoxDecoration(color: Colors.lightBlueAccent), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text('Rocky'), + ), + ), + DecoratedBox( + decoration: BoxDecoration(color: Colors.lightBlueAccent), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text('Spot'), + ), + ), + DecoratedBox( + decoration: BoxDecoration(color: Colors.lightBlueAccent), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text('Fido'), + ), + ), + ], + ), + ), + ), + ); + } +} + +``` + +I use a widget called SizedBox to add blank space between them, which results in this: + +<DashImage figure src="images/1NW7SMJnwzENu3avGQpDgAg.webp" /> + + +But you know, I’ve got a lot of repeated code(also called *boilerplate*) in these three name boxes. Wouldn’t it be great if I could make my own widget that takes a name and handled the details for me? + +Well, I can. + +I’ll make a StatelessWidget, and call it DogName. A stateless widget is a widget that’s composed of children (which is why it contains a build() method) and doesn’t contain any mutable state that it needs to track. When I say mutable state, I mean any properties that change over time. For example, a text box containing a string that the user updates, or a data stream that updates an arrivals/departures display. + +```dart +class DogName extends StatelessWidget { + @override + Widget build(BuildContext context) { + } +} +``` + +This widget doesn’t have any of that. It just needs a string for a name, which won’t change, so StatelessWidget is a perfect fit. I can even make this string final, too. + +```dart +class DogName extends StatelessWidget { + final String name; + + @override + Widget build(BuildContext context) { + } +} +``` + +I can specify the string via the constructor, and because all of its properties are final, I can mark this as a const constructor. + +```dart +class DogName extends StatelessWidget { + final String name; + +const DogName(this.name); + + @override + Widget build(BuildContext context) { + } +} + +``` + +Now, I just need to define the build method using the same widgets, only now the Text widget displays the string from the widget’s name property. + +```dart +class DogName extends StatelessWidget { + final String name; + +const DogName(this.name); + + @override + Widget build(BuildContext context) { + return DecoratedBox( + decoration: BoxDecoration(color: Colors.lightBlueAccent), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(name), + ), + ); + } +} + +``` + +I use this widget to simplify the original code. + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(new DogApp()); +} + +class DogApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'My Dog App', + home: Scaffold( + appBar: AppBar( + title: Text('Yellow Lab'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + DogName('Rocky'), + SizedBox(height: 8.0), + DogName('Spot'), + SizedBox(height: 8.0), + DogName('Fido'), + ], + ), + ), + ), + ); + } +} + +``` + +As you can see, this results in the same UI, but the code is tighter thanks to StatelessWidget and Flutter’s use of composition. + +<DashImage figure src="images/1NW7SMJnwzENu3avGQpDgAg.webp" /> + + +## Part 2 — Widget Trees and Element Trees + +So that’s a little example of how composing with StatelessWidget works. At this point, you might ask yourself, “I see how these build methods work, but when do they get called?” Well, let’s start with just a single DogName widget. + +We tend to think of apps built with Flutter as a tree of widgets, and that’s not a bad thing. But as I mentioned at the beginning, widgets are really just configurations for pieces of an app’s UI. They’re blueprints. So what are these configurations for? Elements. An element is a widget that’s been made real and mounted onscreen. The element tree represents what is actually displayed on the device at any given moment. + +Each widget class has both a corresponding element class and a method to create an instance. + +<DashImage figure src="images/1gG5opaiBFJ0A1ka9tZMeSQ.webp" /> + + +StatelessWidget, for example, creates a StatelessElement. + +When a widget is mounted to the tree, Flutter calls the createElement() method. Flutter asks the widget for an element, and pops that element onto the element tree with a reference back to the widget that created it. + +StatefulElement then asks “I wonder if I have any children?” and calls the Widget’s build() method. + +<DashImage figure src="images/10IZb-Rcyw4yBo3CXQM5zTg.webp" /> + + +In this app, it has several. These widgets then create their own corresponding elements. + +<DashImage figure src="images/17qIL1q-ZP005QS_zTt0j5A.webp" /> + + +And those are mounted to the element tree as well. + +<DashImage figure src="images/1O-D8lfgoAmMqHViU2dWuyg.webp" /> + + +So the app now has two trees: one that represents what’s actually on the screen (the elements), and one that holds the blueprints they were made from (the widgets). + +Now you might be wondering what starts the process of building and creating elements, what kicks off the whole thing, so to speak. Let me show you something you may not have noticed back at the beginning… + +The DogApp class, which represents the entire app, is itself a StatelessWidget. + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(new DogApp()); +} + +class DogApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'My Dog App', + home: Scaffold( + appBar: AppBar( + title: Text('Yellow Lab'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + DogName('Rocky'), + SizedBox(height: 8.0), + DogName('Spot'), + SizedBox(height: 8.0), + DogName('Fido'), + ], + ), + ), + ), + ); + } +} + +``` + +I told you that Widgets can do almost everything, right? If you look at main(), which is the entry point for the app, you can see that it calls the runApp() method, and that’s the starting point. The runApp() method takes a widget and mounts it as the app’s root element with height and width constraints that match the size of the screen. + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(new DogApp()); +} + +class DogApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'My Dog App', + home: Scaffold( + appBar: AppBar( + title: Text('Yellow Lab'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + DogName('Rocky'), + SizedBox(height: 8.0), + DogName('Spot'), + SizedBox(height: 8.0), + DogName('Fido'), + ], + ), + ), + ), + ); + } +} + +``` + +Then, Flutter progresses through all of the build() methods in the widget tree, creating widgets and using them to make elements, until everything is built, mounted onscreen, and ready to be laid out and rendered. + +<DashImage figure src="images/0QLq-DId9PheGXmk9.webp" /> + + +Which is how it displays three little boxes containing the names of the yellow labs. + +<DashImage figure src="images/1lRBiFALMxP8-RPAsmlk7pw.webp" /> + + +So that’s an introduction to composing with StatelessWidgets and building an interface. One thing I didn’t mention is how to update or rebuild an interface when the data changes. That’s because StatelessWidgets don’t do that. They’re stateless, so they can’t track data over time, or trigger rebuilds on their own. + +Fortunately, Flutter also has StatefulWidgets, which we’ll tell you about in the next episode of this series. + +For more information about Flutter and all of its many widgets, head to [flutter.io](http://flutter.io). + +You can also [watch other episodes in the series here](http://bit.ly/FlutterWidgets101) or [try a Flutter codelab](http://bit.ly/Flutter-Codelab). \ No newline at end of file diff --git a/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/0I294y1ZUpA3AGe2G.webp b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/0I294y1ZUpA3AGe2G.webp new file mode 100644 index 0000000000..1868959481 Binary files /dev/null and b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/0I294y1ZUpA3AGe2G.webp differ diff --git a/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/0O9felhcXAjXxjKcQ.webp b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/0O9felhcXAjXxjKcQ.webp new file mode 100644 index 0000000000..0e07fcff3e Binary files /dev/null and b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/0O9felhcXAjXxjKcQ.webp differ diff --git a/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/0dYdomk1To1NJBTCp.webp b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/0dYdomk1To1NJBTCp.webp new file mode 100644 index 0000000000..94434e86e4 Binary files /dev/null and b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/0dYdomk1To1NJBTCp.webp differ diff --git a/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/10_1C8Xaq_vCvf0DGmshNiw.webp b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/10_1C8Xaq_vCvf0DGmshNiw.webp new file mode 100644 index 0000000000..ce5e5a8641 Binary files /dev/null and b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/10_1C8Xaq_vCvf0DGmshNiw.webp differ diff --git a/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/13IBbh6w93WVhHZ4i0asjUQ.webp b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/13IBbh6w93WVhHZ4i0asjUQ.webp new file mode 100644 index 0000000000..366ea93601 Binary files /dev/null and b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/13IBbh6w93WVhHZ4i0asjUQ.webp differ diff --git a/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/18LUuPonWPA0T7yf2cNx7aQ.webp b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/18LUuPonWPA0T7yf2cNx7aQ.webp new file mode 100644 index 0000000000..01d1afdca6 Binary files /dev/null and b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/18LUuPonWPA0T7yf2cNx7aQ.webp differ diff --git a/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1C_XdNuBceIRYcA76Ig8NTA.webp b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1C_XdNuBceIRYcA76Ig8NTA.webp new file mode 100644 index 0000000000..66446f7bfb Binary files /dev/null and b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1C_XdNuBceIRYcA76Ig8NTA.webp differ diff --git a/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1G8q-b4kRipk4uR3TRtWQNQ.webp b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1G8q-b4kRipk4uR3TRtWQNQ.webp new file mode 100644 index 0000000000..fcba45db4b Binary files /dev/null and b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1G8q-b4kRipk4uR3TRtWQNQ.webp differ diff --git a/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1LcKvZCtlUTxX31rFxF8c8A.webp b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1LcKvZCtlUTxX31rFxF8c8A.webp new file mode 100644 index 0000000000..20199911bd Binary files /dev/null and b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1LcKvZCtlUTxX31rFxF8c8A.webp differ diff --git a/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1N6UBt6twhPEZp7OYxBdoEw.webp b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1N6UBt6twhPEZp7OYxBdoEw.webp new file mode 100644 index 0000000000..d34022e63e Binary files /dev/null and b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1N6UBt6twhPEZp7OYxBdoEw.webp differ diff --git a/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1TgBQisZYEvQbDnntk9-a9Q.webp b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1TgBQisZYEvQbDnntk9-a9Q.webp new file mode 100644 index 0000000000..372f05543e Binary files /dev/null and b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1TgBQisZYEvQbDnntk9-a9Q.webp differ diff --git a/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1ZoMUEbRIQoIzKGtB82VIZg.webp b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1ZoMUEbRIQoIzKGtB82VIZg.webp new file mode 100644 index 0000000000..29c499750b Binary files /dev/null and b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1ZoMUEbRIQoIzKGtB82VIZg.webp differ diff --git a/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1bZaSpjwB5GCoM7wpSrgR6w.webp b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1bZaSpjwB5GCoM7wpSrgR6w.webp new file mode 100644 index 0000000000..56f9d2cd96 Binary files /dev/null and b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1bZaSpjwB5GCoM7wpSrgR6w.webp differ diff --git a/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1oWDuoKe1gCDL5UPrQUwePg.webp b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1oWDuoKe1gCDL5UPrQUwePg.webp new file mode 100644 index 0000000000..4721004857 Binary files /dev/null and b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/images/1oWDuoKe1gCDL5UPrQUwePg.webp differ diff --git a/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/index.md b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/index.md new file mode 100644 index 0000000000..95b279183b --- /dev/null +++ b/sites/www/content/blog/how-to-debug-layout-issues-with-the-flutter-inspector/index.md @@ -0,0 +1,425 @@ +--- +title: "How to debug layout issues with the Flutter Inspector" +description: "Exploring why they occur and how to solve them" +publishDate: 2020-07-27 +author: ktjlee +image: images/1oWDuoKe1gCDL5UPrQUwePg.webp +category: tutorial +layout: blog +--- +> Note: Being familiar with `Row`, `Column`, and `Expanded` is useful, but not required, for this article. + +As a Flutter developer, you’ve probably run into issues where an image is cut off (or not even visible) on your app. Maybe you got the “viewport was given unbounded height” error.” In fact, the two most frequent types of Flutter errors are both problems with layout: widget overflow and “renderbox not laid out” issues. You’re not alone in running into layout problems, but, where do you go to solve them? + +<DashImage figure src="images/1oWDuoKe1gCDL5UPrQUwePg.webp" /> + + +Fortunately, the Dart DevTool’s [Flutter Inspector](https://flutter.dev/docs/development/tools/devtools/inspector) can help you understand *why* they occur and also *how* you can solve them. **In this article, you learn how to use the tool by debugging 3 common layout issues. So, the next time you encounter an issue, you can solve it like a pro!** + +## **Table of Contents** + +1. [What is the Flutter Inspector?](#042b) + +1. [The debugging adventure](#37f6) +• [Overflow](#738b) +• [Unbounded height error](#1de2) +• [Invisible VerticalDivider](#631b) + +1. [Summary](#04f5) + +## What is the Flutter Inspector? + +The Flutter Inspector is a tool (part of the [Dart DevTools](https://flutter.dev/docs/development/tools/devtools/overview) suite) for exploring and visualizing widget trees. It’s perfect for discovering the ins and outs of your app layout. Take a peek at the tool in the following GIF: + +<DashImage figure src="images/1ZoMUEbRIQoIzKGtB82VIZg.webp" alt="A GIF showing the Details Tree and Layout Explorer features of the Flutter Inspector" caption="A GIF showing the Details Tree and Layout Explorer features of the Flutter Inspector" /> + + +With the inspector, you can select widgets on the app or even remove the debug banner. It’s got you covered whether you’re wondering why your widget is invisible or curious about how adding flex to a **Row**’s child affects the UI. The article focuses on the following features: + +* **Details Tree —** Enables you to examine the properties of each widget. You can examine a widget’s actual *size* and see how *constraints* are passed down from the parent widget. + +* **Layout Explorer —** Enables you to visualize [`Flex`](https://api.flutter.dev/flutter/widgets/Flex-class.html) widgets (`Row`, `Column`, `Flex`) and their children. Adjusting the flex, fit, and axis alignment allows you to see the changes on your running app. + +At this point, you might be wondering, “How do I try this awesome tool?” Just run Dart DevTools on the app, and the Inspector is the first tool you see. For more information, see [how to run Dart DevTools from your IDE or the command line](https://flutter.dev/docs/development/tools/devtools/overview#how-do-i-install-devtools). + +## The debugging adventure 🤠 + +Let’s start with a demo app containing 3 layout problems that you will debug. You’ll use the Flutter Inspector to debug each issue separately, and combine the fixed issues at the end to complete a simple menu app that looks like the following screenshot + +<DashImage figure src="images/18LUuPonWPA0T7yf2cNx7aQ.webp" alt="End menu app with the fixed issues" caption="End menu app with the fixed issues" /> + + +Use the following steps when approaching a layout issue. To help you remember, use the COIN acronym. Who coined this term? I did, just now. + +1. **C**heck the error message on the debug console to identify the error type and error-causing widget. + +1. **O**pen the **Layout Explorer** to visualize a `Flex` widget and its children. + +1. **I**nspect the size and constraints of the error-causing widget and its parent/children with the **Details Tree**. + +1. **N**avigate back to your code and fix the issue. + +It’s best if you follow along the rest of the article on your own computer. So, open your favorite text editor or IDE, and let’s go on this adventure together! + +1. Create a new Flutter project called `menu`. + +``` +$ flutter create menu +``` + + +2. Replace the `lib/main.dart` file content. +Each layout issue is separated into its own `Example` ****class in the code, starting with `Example1` in the body of the app. Replace `lib/main.dart` with the following code: + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(Menu()); +} + +class MenuItem extends StatelessWidget { + const MenuItem(this.icon, this.itemText); + final String icon; + final String itemText; + @override + Widget build(BuildContext context) { + return ListTile( + leading: Text( + icon, + style: TextStyle( + fontSize: 30.0, + ), + ), + title: Text(itemText), + ); + } +} + +class Menu extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + home: Scaffold( + appBar: AppBar( + title: Text('Menu Demo'), + ), + body: Padding( + padding: EdgeInsets.all(20.0), + child: Column( + children: [ + // Modify code here + Example1(), + ], + ), + ), + ), + ); + } +} + +// Problem 1: Overflow error +class Example1 extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.only(bottom: 30.0), + child: Row( + children: [ + Text( + 'Explore the restaurant\'s delicious menu items below!', + style: TextStyle( + fontSize: 18.0, + ), + ), + ], + ), + ); + } +} + +// Problem 2: Viewport was given unbounded height error +class Example2 extends StatelessWidget { + @override + Widget build(BuildContext context) { + return ListView( + children: [ + MenuItem('🍔', 'Burger'), + MenuItem('🌭', 'Hot Dog'), + MenuItem('🍟', 'Fries'), + MenuItem('🥤', 'Soda'), + MenuItem('🍦', 'Ice Cream'), + ], + ); + } +} + +// Problem 3: Invisible VerticalDivider +class Example3 extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + RaisedButton( + onPressed: () { + print('Pickup button pressed.'); + }, + child: Text( + 'Pickup', + ), + ), + // This widget is not shown on screen initially. + VerticalDivider( + width: 20.0, + thickness: 5.0, + ), + RaisedButton( + onPressed: () { + print('Delivery button pressed.'); + }, + child: Text( + 'Delivery', + ), + ) + ], + ); + } +} + +``` + +3. Run the app. + +4. Open [Dart DevTools](https://flutter.dev/docs/development/tools/devtools/overview#how-do-i-install-devtools). + +## Layout problem 1: Overflow error + +When you run the app, you see a yellow and black diagonally striped box at the end of the line, similar to [caution tape](https://www.google.com/search?q=caution+tape&sxsrf=ALeKk00_Sm0OMCspL5iNnp6rb6hX6KKvjg:1595272791540&source=lnms&tbm=isch&sa=X&ved=2ahUKEwjevbygxtzqAhW7HzQIHX11APAQ_AUoAXoECA0QAw&biw=807&bih=351): + +<DashImage figure src="images/1G8q-b4kRipk4uR3TRtWQNQ.webp" /> + + +This means that there’s an overflow error — the most common Flutter layout error. Now, let’s follow the debugging steps to identify the problem and find the right fix. + +**1. Check the error message on the console.** + +<DashImage figure src="images/1N6UBt6twhPEZp7OYxBdoEw.webp" alt="Overflow error on the Debug Console" caption="Overflow error on the Debug Console" /> + + +First, identify which widget is causing the problem The error message indicates that the `Row` on line 54 of `main.dart` is causing the problem. Because `Row` is a `Flex` widget (meaning that`Row` extends the `Flex` class), you can examine it with the **Layout Explorer**. + +**2. Open the Layout Explorer.** + +Navigate to DevTools and open the **Layout Explorer** tab. + +<DashImage figure src="images/10_1C8Xaq_vCvf0DGmshNiw.webp" alt="Overflow error on the Layout Explorer" caption="Overflow error on the Layout Explorer" /> + + +Click **Row**. (The numbers on the image correlate to the following steps.) + +1. Red banners appear at the bottom, indicating that something is wrong. When you take a closer look at the banners, you realize that **Text** (width = 447) is *wider than the parent widget*, **Row** (width=335), causing the overflow error. + +1. You need a way to tell **Text** that it can only be as wide as **Row** and no more. Try adjusting the flex of **Text** to 1. (This is similar to wrapping `Text` with `Expanded`.) As a result, the **Text** shrinks, and the red banners disappear. Phew, it looks like it’s fixed. Not quite! You still have to update the code because the tool doesn’t touch your code. It just shows *what would happen* if you changed some layout properties. +> Aside: You might wonder, why aren’t all of `Row`’s and `Column`’s children `Expanded` by default? +> The Flutter team made that design decision. If all the children were `Expanded` by default, it creates other layout problems like some children being too squeezed or stretched. + +**3. Inspect the size and constraints with the Details Tree.** +In this scenario, you can skip this step because the problem has been identified. + +**4. Navigate back to the code and fix it.** + +<DashImage figure src="images/13IBbh6w93WVhHZ4i0asjUQ.webp" alt="Wrap Text with Expanded using smart refactoring on VS Code (similar method on other editors)" caption="Wrap Text with Expanded using smart refactoring on VS Code (similar method on other editors)" /> + + +Wrap `Text` with `Expanded`. The default flex is 1, so you don’t have to specify this property. + +## Layout problem 2: Unbounded height error + +Let’s move on to the next example by replacing `Example1()` inside the Column with `Example2()` and hot reloading. + +``` +Column( + children: [ + // Modify code here + Example2(), + ], +) +``` + + +Nothing displays on the app, despite having a `ListView` with various menu items in the `Example2` class: + +<DashImage figure src="images/1LcKvZCtlUTxX31rFxF8c8A.webp" /> + + +What’s going on? + +**1. Check the error message on the console.** + +<DashImage figure src="images/1bZaSpjwB5GCoM7wpSrgR6w.webp" alt="Unbounded height error on the Debug Console" caption="Unbounded height error on the Debug Console" /> + + +`ListView` on line 72 of `main.dart` is causing the “Vertical viewport was given unbounded height” error. At first glance, the terms *vertical viewport* and *unbounded* are unclear, so continue to the next step. + +**2. Open the Layout Explorer.** + +Navigate back to DevTools and open the **Layout Explorer** tab. + +<DashImage figure src="images/1TgBQisZYEvQbDnntk9-a9Q.webp" alt="Layout Explorer doesn’t display grandchildren of Flex widgets." caption="Layout Explorer doesn’t display grandchildren of Flex widgets." /> + + +Refresh the tree by clicking on the refresh icon at the top. Nothing appears after clicking **ListView** because the **Layout Explorer** only supports `Flex` widgets and their direct children. Interestingly, clicking **Example2** and **Column** doesn’t do the trick either — the **Layout Explorer** is still blank. Move on to the next step. + +**3. Inspect the size and constraints with the Details Tree.** + +<DashImage figure src="images/0I294y1ZUpA3AGe2G.webp" alt="ListView’s constraints and size on the Details Tree" caption="ListView’s constraints and size on the Details Tree" /> + + +Expand `ListView`’s first `renderObject`, which contains the information for painting the widget. + +1. The orange text indicates the size is missing — no wonder the `ListView` is missing from the app. + +1. After looking at the `constraints` property, notice that the height constraint is listed as infinity. The error message makes more sense now. `ListView` is a “viewport” given an *unbounded* — [in other words infinite](https://api.flutter.dev/flutter/rendering/BoxConstraints-class.html) — height in its scrolling direction. + +Constraints are passed down from the parent widget. So, here’s a snapshot of how the widgets determine their constraints: + +`Column`: Take up as much height as you want. + +`ListView`: Okay, I’ll take up all the space. + +`Column`: Whoa, but that’s like *infinity*, dude. + +And, the widgets don’t know what to do … size can’t be determined because `ListView` wants infinite height, which can’t be depicted on screen. +> Aside: Why doesn’t `Column` just limit the height of its children to its own height? +> You can end up in a situation where the first child takes up all the space, forcing the second child to have 0 height. And you wouldn’t immediately know about it because no errors are thrown. + +**4. Navigate back to your code and fix it.** + +``` +class Example2 extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Expanded( + child: ListView( + ... + ), + ); + } +} +``` + + +Remember from earlier that **wrapping a widget with** `Expanded` **gives it a *bounded* constraint along the parent’s main axis** (width for `Row`, height for `Column`). In this case, the parent is `Column`, so `Expanded` provides a height constraint. Wrap the `ListView` with `Expanded`, and hot reload to see the list show up on the app. + +## Layout problem 3: Invisible VerticalDivider + +Now, replace `Example2()` in the `Column` with `Example3()`. + +``` +Column( + children: [ + // Modify code here + Example3(), + ], +) +``` + + +Take a close look at the `Example3` class in the code. Notice that the `VerticalDivider` exists, but only two buttons appear on the app after hot reloading: + +<DashImage figure src="images/1C_XdNuBceIRYcA76Ig8NTA.webp" /> + + +Why is the `VerticalDivider` invisible? + +**1.** **Check the error message on the console.** +You didn’t get any error messages this time. Continue to the next step. + +**2. Open the Layout Explorer.** +Navigate back to DevTools and click on the **Layout Explorer** tab. + +<DashImage figure src="images/0O9felhcXAjXxjKcQ.webp" alt="VerticalDivider on the Layout Explorer" caption="VerticalDivider on the Layout Explorer" /> + + +After refreshing the tree, click **VerticalDivider** and scroll to the right side of the **Layout Explorer.** Observe that **VerticalDivider**’s width and height are unconstrained. + +1. Notice that the **VerticalDivider**’s height is 0, which explains why it isn’t displayed on the app. + +1. Toggle the flex to 1, like you did before. The height remains 0. Wrapping a `VerticalDivider` with `Expanded` won’t do the job in this case because `Expanded` provides a width constraint, instead of a height constraint. + +1. The next thing you might try is to stretch the height of the divider to the height of the raised buttons, so try setting the cross-axis alignment to stretch. The height remains 0, so move on to the next step. + +**3. Inspect the size and constraints with the Details Tree.** + +<DashImage figure src="images/0dYdomk1To1NJBTCp.webp" alt="Inspecting Row and its children using the Details Tree" caption="Inspecting Row and its children using the Details Tree" /> + + +1. Open the first `renderObject` under `VerticalDivider`. The constraints property indicates that the widget has neither width nor height constraints, matching what the **Layout Explorer** displays. But, under `additionalConstraints`, the width is 20 (as explicitly defined in the sample code), but height still has no constraints. The width isn’t the problem here, so let’s focus on the height. + +1. Go up to the parent widget, `Row`, and open the `renderObject` to see that `Row` also doesn’t have a height constraint. + +Why? + +The most important thing to remember is that *constraints are passed down:* + +`Column` tells `Row`: Choose any height you want. + +`Row` tells `VerticalDivider`: Choose any width you want. Because `Column` told me to choose my height, you can also choose your own height. + +`VerticalDivider`: The width property is passed in, so my width is 20. I can choose my height, so I’ll default to 0. + +**4. Navigate back to your code and fix it.** + +``` +class Example3 extends StatelessWidget { + @override + Widget build(BuildContext context) { + return SizedBox( + height: 50.0, + child: Row( + ... + ), + ); + } +} +``` + + +For `VerticalDivider` to have a height, a height constraint must be given. Wrap `Row` in `SizedBox` and give it a fixed height of 50.0. Doing so forces `Row` to pass a height constraint to the `VerticalDivider`. + +Hot reload. Voila! `VerticalDivider` pops up on the screen. +> Aside: `VerticalDivider` behaves differently from `ListView` because of their unique definitions. When told to choose their own heights, `ListView` wants to be as tall as possible whereas the `VerticalDivider` wants to be as short as possible. But, both need a height constraint to properly appear in the app! + +Now, let’s put the fixed code from all 3 examples together inside the `Column`: + +``` +Column( + children: [ + // Modify code here + Example1(), + Example2(), + Example3(), + ], +) +``` + + +Hot reload. Congratulations, you completed the menu app! + +## Summary + +Through this tutorial, you learned: + +* Constraints are passed down the widget tree. + +* `Expanded` ****provides *bounded* constraints to a `Row` or `Column`’s child. + +* Flutter Inspector is your best friend when dealing with layout issues. + +To learn more, check out [understanding constraints](https://flutter.dev/docs/development/ui/layout/constraints) on flutter.dev. + +Happy debugging! + +*About the author: Katie is a rising senior at University of Michigan studying Computer Science. She’s currently an intern on the Flutter Developer Relations team, helping developers learn and build awesome apps. To see what she’s up to, visit her [GitHub](https://github.com/katisari) and [LinkedIn](http://www.linkedin.com/in/katiesaari).* \ No newline at end of file diff --git a/sites/www/content/blog/how-to-embed-a-flutter-application-in-a-website-using-dartpad/images/0botL1tnoYh9nbjgw.webp b/sites/www/content/blog/how-to-embed-a-flutter-application-in-a-website-using-dartpad/images/0botL1tnoYh9nbjgw.webp new file mode 100644 index 0000000000..b203e2cddb Binary files /dev/null and b/sites/www/content/blog/how-to-embed-a-flutter-application-in-a-website-using-dartpad/images/0botL1tnoYh9nbjgw.webp differ diff --git a/sites/www/content/blog/how-to-embed-a-flutter-application-in-a-website-using-dartpad/images/0u979IALi6tiDee8O.webp b/sites/www/content/blog/how-to-embed-a-flutter-application-in-a-website-using-dartpad/images/0u979IALi6tiDee8O.webp new file mode 100644 index 0000000000..c0a1dc681b Binary files /dev/null and b/sites/www/content/blog/how-to-embed-a-flutter-application-in-a-website-using-dartpad/images/0u979IALi6tiDee8O.webp differ diff --git a/sites/www/content/blog/how-to-embed-a-flutter-application-in-a-website-using-dartpad/index.md b/sites/www/content/blog/how-to-embed-a-flutter-application-in-a-website-using-dartpad/index.md new file mode 100644 index 0000000000..1f30b51ac7 --- /dev/null +++ b/sites/www/content/blog/how-to-embed-a-flutter-application-in-a-website-using-dartpad/index.md @@ -0,0 +1,188 @@ +--- +title: "How to embed a Flutter application in a Website using DartPad" +description: "Have you ever wanted to embed your mobile application into a website? This is now easy to do with Flutter." +publishDate: 2020-03-23 +author: josealba +image: images/0u979IALi6tiDee8O.webp +category: tutorial +layout: blog +--- + +*About the author: Jose recently graduated from university and now works on [Material](https://material.io), a design system that helps teams build high-quality digital experiences. Jose’s team maintains the Flutter [material library](https://api.flutter.dev/flutter/material/material-library.html).* + +Have you ever wanted to embed your mobile application into a website? This is now easy to do with Flutter. + +Flutter is a framework that lets you create native applications for iOS and Android and, most recently, for the web! This means that you can now develop a website using Flutter. [DartPad,](https://dartpad.dev/) the open source tool that lets you run Dart in any modern browser, has also been extended to support Flutter applications. You can now make a simple application on the web and share it with others, instead of hosting the Flutter application somewhere and then embedding it into a web page. + +This feature is useful and powerful, as you can imagine. Users can simultaneously see both the code and running application. Here are a list of steps you can follow to embed your Flutter application. There are two ways to create a DartPad instance using GitHub: + +1. Using a Gist file + +1. Using a GitHub repo + +## Using a Gist file + +1. To get started, open [DartPad](https://dartpad.dev/) and create the application you want to share with others. The code you write here represents the main.dart file in a Flutter application. You can also use an editor to do this but be aware that DartPad does have some limitations. For example, you are limited to one Dart file, a default pubspec, and you can’t import non-Dart libraries or plugins.. + +This article uses the following code: + +```dart +import 'package:flutter/material.dart'; + +void main() => runApp(MyApp()); + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + debugShowCheckedModeBanner: false, + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: MyHomePage(), + ); + } +} + +class MyHomePage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Flutter Applcation Embedded in Website'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + Text( + 'The Gist URL to this website is:', + textAlign: TextAlign.center, + ), + Text( + 'https://gist.github.com/JoseAlba/a205e21aa0d0a64fe7c977107ce22cbf \n', + textAlign: TextAlign.center, + ), + Text( + 'The Dartpad URL to this website is:', + textAlign: TextAlign.center, + ), + Text( + 'https://dartpad.dev/a205e21aa0d0a64fe7c977107ce22cbf \n', + textAlign: TextAlign.center, + ), + Text( + 'The iFrame code is:', + textAlign: TextAlign.center, + ), + Text( + '<iframe src="https://dartpad.dev/embed-flutter.html?id=a205e21aa0d0a64fe7c977107ce22cbf"></iframe> \n', + textAlign: TextAlign.center, + ), + Text( + 'This Flutter application might be within an iFrame within a Medium post. \n', + textAlign: TextAlign.center, + ), + ], + ), + ), + ); + } +} + +``` + +Note that importing ‘package:flutter/material.dart’ tells DartPad that this is a Flutter app. + +Once your Flutter application builds in DartPad, copy this code and paste it into a [new gist file](https://gist.github.com/) on GitHub. This gist can be either public or private. A gist is a git repository for simple purposes like sharing code. The file name of your gist must be main.dart . For reference, see my [main.dart gist](https://gist.github.com/JoseAlba/a205e21aa0d0a64fe7c977107ce22cbf) file. If you are having problems, see the [DartPad Sharing Guide](https://github.com/dart-lang/dart-pad/wiki/Sharing-Guide). + +[https://gist.github.com/JoseAlba/a205e21aa0d0a64fe7c977107ce22cbf](https://gist.github.com/JoseAlba/a205e21aa0d0a64fe7c977107ce22cbf) + +You can test if you did this correctly by checking if your gist is available on DartPad. To do this, replace everything before the gistID with “dartpat.dev/”. For example, to view “[https://gist.github.com/JoseAlba/a205e21aa0d0a64fe7c977107ce22cbf](https://gist.github.com/JoseAlba/a205e21aa0d0a64fe7c977107ce22cbf)” in DartPad, use the URL, “[https://dartpad.dev/a205e21aa0d0a64fe7c977107ce22cbf](https://dartpad.dev/a205e21aa0d0a64fe7c977107ce22cbf)” + +<DashImage figure src="images/0u979IALi6tiDee8O.webp" /> + + +To embed your Flutter application, prefix the gist ID with “[https://dartpad.dev/embed-flutter.html?id=](https://dartpad.dev/embed-flutter.html?id=a205e21aa0d0a64fe7c977107ce22cbf)”. For example, to view “[https://gist.github.com/JoseAlba/a205e21aa0d0a64fe7c977107ce22cbf](https://gist.github.com/JoseAlba/a205e21aa0d0a64fe7c977107ce22cbf) “in an HTML iframe, use “[https://dartpad.dev/embed-flutter.html?id=a205e21aa0d0a64fe7c977107ce22cbf](https://dartpad.dev/embed-flutter.html?id=a205e21aa0d0a64fe7c977107ce22cbf)” . You can learn more details about embedding DartPad code into a website in the [DartPad Embedding Guide](https://github.com/dart-lang/dart-pad/wiki/Embedding-Guide). I recommend you visit this page since it also explains the query parameters. The following example uses an <iframe> element: + +``` +<div style=”position:relative;padding-top:56.25%;”> +<iframe src=’https://dartpad.dev/embed-flutter.html?id=d&theme=dark&run=true&split=50' style=”position:absolute;top:0;left:0;width:100%;height:100%;”>. </iframe> +</div> +``` + + +This code created the screenshot below: + +<DashImage figure src="images/0botL1tnoYh9nbjgw.webp" /> + + +## Using a GitHub repo + +To get started, you need to create a repository. Within this repository there are a few files that you can have. + +* dartpad_metadata.yaml (MANDATORY) + +* hint.txt (OPTIONAL) + +* main.dart (MANDATORY) + +* solution.dart (OPTIONAL) + +* test.dart (OPTIONAL) + +If you want to have only one Dart file, all you need are the dartpad_metadata.yaml and main.dart files, but you have the freedom to use other Dart files, too. In my case, I wanted to create the main.dart file without having the runApp() method within it, so I use test.dart to run the application. My repo consists of 3 files: + +* [dartpad_metadata.yaml](https://github.com/JoseAlba/flutter_code/blob/master/lib/dartpad/dartpad_metadata.yaml) + +* [main.dart](https://github.com/JoseAlba/flutter_code/blob/master/lib/dartpad/main.dart) + +* [test.dart](https://github.com/JoseAlba/flutter_code/blob/master/lib/dartpad/test.dart) + +Note that test.dart doesn’t require any imports. This is because DartPad sees main.dart and test.dart as one single file. So, if I import the Material package within main.dart, it will be seen in test.dart once the DartPad instance is loaded up. + +To load this folder into DartPad you need to know the following: + +* gh_owner: Owner of the GitHub account. + +* gh_repo: Name of the repo within the above account. + +* gh_path: Path to a dartpad_metadata.yaml file within the repo. + +* gh_ref: (optional) Branch to use when loading the file. Defaults to master. + +In my case: + +* gh_owner: JoseAlba + +* gh_repo: flutter_code + +* gh_path: lib/dartpad + +* The gh_ref is not needed since I am using the master branch + +This means if I want to load: + +[https://github.com/JoseAlba/flutter_code/blob/master/lib/dartpad/main.dart](https://github.com/JoseAlba/flutter_code/blob/master/lib/dartpad/main.dart) + +The DartPad URL would be: + +[dartpad.dev/embed-flutter.html?gh_owner=JoseAlba&gh_repo=flutter_code&gh_path=lib/dartpad](http://dartpad.dev/embed-flutter.html?gh_owner=JoseAlba&gh_repo=flutter_code&gh_path=lib/dartpad) + +1. To embed a flutter application into an iframe, specify the following: + +``` +<div style=”position:relative;padding-top:56.25%;”> + +<iframe src=’https://dartpad.dev/embed-flutter.html?gh_owner=JoseAlba&gh_repo=flutter_code&gh_path=lib/dartpad&theme=dark&run=true&split=50' style=”position:absolute;top:0;left:0;width:100%;height:100%;”></iframe> + +</div> +``` + + +## In Conclusion + +DartPad is a powerful tool that helps you test, share, and embed your code into web pages. If you want to learn more. see the [DartPad wiki](https://github.com/dart-lang/dart-pad/wiki). + +*To learn more about Jose, visit his pages on [GitHub](https://github.com/JoseAlba), [LinkedIn](https://www.linkedin.com/in/josealba1996/), [YouTube](https://www.youtube.com/channel/UCOdKA_On0oPe1tz02z1QfxA?view_as=subscriber), and [Instagram](https://www.instagram.com/jose.alba/).* \ No newline at end of file diff --git a/sites/www/content/blog/how-to-float-an-overlay-widget-over-a-possibly-transformed-ui-widget/images/0FB5HpEeEvZ6NjJXl.webp b/sites/www/content/blog/how-to-float-an-overlay-widget-over-a-possibly-transformed-ui-widget/images/0FB5HpEeEvZ6NjJXl.webp new file mode 100644 index 0000000000..5e874e2341 Binary files /dev/null and b/sites/www/content/blog/how-to-float-an-overlay-widget-over-a-possibly-transformed-ui-widget/images/0FB5HpEeEvZ6NjJXl.webp differ diff --git a/sites/www/content/blog/how-to-float-an-overlay-widget-over-a-possibly-transformed-ui-widget/images/178wqXwvzHdc-vJ9lxtazkQ.webp b/sites/www/content/blog/how-to-float-an-overlay-widget-over-a-possibly-transformed-ui-widget/images/178wqXwvzHdc-vJ9lxtazkQ.webp new file mode 100644 index 0000000000..aa85051963 Binary files /dev/null and b/sites/www/content/blog/how-to-float-an-overlay-widget-over-a-possibly-transformed-ui-widget/images/178wqXwvzHdc-vJ9lxtazkQ.webp differ diff --git a/sites/www/content/blog/how-to-float-an-overlay-widget-over-a-possibly-transformed-ui-widget/index.md b/sites/www/content/blog/how-to-float-an-overlay-widget-over-a-possibly-transformed-ui-widget/index.md new file mode 100644 index 0000000000..5780b39fe6 --- /dev/null +++ b/sites/www/content/blog/how-to-float-an-overlay-widget-over-a-possibly-transformed-ui-widget/index.md @@ -0,0 +1,268 @@ +--- +title: "How to float an overlay widget over a (possibly transformed) UI widget" +description: "Say you have a widget in your app’s UI and you want to overlay a floating widget on top of that widget. Maybe that widget is rotated or…" +publishDate: 2020-03-02 +author: josealba +image: images/0FB5HpEeEvZ6NjJXl.webp +category: tutorial +layout: blog +--- + +*About the author: Jose recently graduated from university and now works on [Material](https://material.io), a design system that helps teams build high-quality digital experiences. Jose’s team maintains the Flutter [material library](https://api.flutter.dev/flutter/material/material-library.html).* + +Say you have a widget in your app’s UI and you want to overlay a floating widget on top of that widget. Maybe that widget is rotated or has other transforms applied. How do you convey the position and transform information of the UI widget to the corresponding widget in the overlay? + +You can do this using the CompositedTransformTarget, CompositedTransformWidget, LayerLink, Overlay, and OverlayEntry widgets. + +In Flutter, the overlay lets you print visual elements on top of other widgets by inserting them into the overlay’s stack. You insert a widget into the overlay using an OverlayEntry and you use Positioned and AnimatedPositioned to choose where the entry is positioned within the overlay. This is useful when you need an item to appear on top of another widget (similar to the Stack widget) but you can use this widget anywhere without having to modify your entire codebase. + +As described in this [article](https://medium.com/saugo360/https-medium-com-saugo360-flutter-using-overlay-to-display-floating-widgets-2e6d0e8decb9), a programmer needs to implement an autosuggestion feature to a text field widget to an existing page. They could implement this feature using a stack but, as mentioned in the article, this approach can be: intrusive, rigorous, error-prone, and just feels wrong. Instead of redesigning your whole screen into a stack, you can use the overlay widget to add this effect. This scenario can happen any time a developer has to implement a new feature on an existing route. + +Using an Overlay might seem intuitive but can be challenging to implement in Flutter. The overlay entry must be inserted using a callback method. Plus you need to keep a reference to the overlay entry since you remove the entry using the reference and not the context of the overlay. Also, if you need the overlay entry’s position and transformation to be dependent on another widget that is *not* in the overlay, it can get confusing. This is because the context of the overlay’s MediaQuery is different from the regular context. You may have seen this when using padding or margin in a Widget before making a call to the Overlay. Luckily, Flutter already takes care of these details for you. + +If an overlay entry needs to follow a ‘target’ that is not on the overlay stack you can use CompositedTransformTarget, CompositedTransformFollower, and LayerLink to “glue” the widgets together To do this you need to: + +* Wrap the widget on the overlay with a CompositedTransformFollower. + +* The follower has to be a descendant of CompositedTransformTarget. + +* The target Widget must be wrapped with a CompositedTransformTarget. + +* A LayerLink instance glues the follower and target together. + +In the following GIF, the blue container is not on the overlay but the green container is. The blue container is a child of CompositedTransformTarget and the green container is the child of CompostedTransformFollower. They are linked together using the same LayerLink instance. Note how the green overlay widget is aware of the bounds of the blue UI widget, even though the blue widget is not in the overlay: + +<DashImage figure src="images/178wqXwvzHdc-vJ9lxtazkQ.webp" /> + + +Try it yourself using this [DartPad](https://dartpad.dev/8fd3ae34c3e5f1932dc687208be6f434) instance. The example code is as follows: + +Code by Hans Muller + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(MaterialApp(home: Slide())); +} + +class Indicator extends StatelessWidget { + Indicator({ Key key, this.link, this.offset }) : super(key: key); + + final LayerLink link; + final Offset offset; + + @override + Widget build(BuildContext context) { + return CompositedTransformFollower( + offset: offset, + link: link, + child: Container( + color: Colors.green, + ), + ); + } +} + +class Slide extends StatefulWidget { + Slide({ Key key }) : super(key: key); + + @override + _SlideState createState() => _SlideState(); +} + +class _SlideState extends State<Slide> { + final double indicatorWidth = 24.0; + final double indicatorHeight = 300.0; + final double slideHeight = 200.0; + final double slideWidth = 400.0; + + final LayerLink layerLink = LayerLink(); + OverlayEntry overlayEntry; + Offset indicatorOffset; + + Offset getIndicatorOffset(Offset dragOffset) { + final double x = (dragOffset.dx - (indicatorWidth / 2.0)).clamp(0.0, slideWidth - indicatorWidth); + final double y = (slideHeight - indicatorHeight) / 2.0; + return Offset(x, y); + } + + void showIndicator(DragStartDetails details) { + indicatorOffset = getIndicatorOffset(details.localPosition); + overlayEntry = OverlayEntry( + builder: (BuildContext context) { + return Positioned( + top: 0.0, + left: 0.0, + child: SizedBox( + width: indicatorWidth, + height: indicatorHeight, + child: Indicator( + offset: indicatorOffset, + link: layerLink + ), + ), + ); + }, + ); + Overlay.of(context).insert(overlayEntry); + } + + void updateIndicator(DragUpdateDetails details) { + indicatorOffset = getIndicatorOffset(details.localPosition); + overlayEntry.markNeedsBuild(); + } + + void hideIndicator(DragEndDetails details) { + overlayEntry.remove(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Overlay Indicator')), + body: Center( + child: CompositedTransformTarget( + link: layerLink, + child: Container( + width: slideWidth, + height: slideHeight, + color: Colors.blue.withOpacity(0.2), + child: GestureDetector( + onPanStart: showIndicator, + onPanUpdate: updateIndicator, + onPanEnd: hideIndicator, + ), + ), + ), + ), + ); + } +} + +``` + +This example shows how to use these widgets together. To see the true power of these widgets, apply a Transformation to the CompositedTransformTarget. You will notice that the green overlay widget is also impacted by this transformation, thanks to their shared LayerLink widget. + +In the following GIF, the blue container is not on the overlay but the green container is. This time the CompositedTransformTarget has been rotated. As you can see, even though the CompositeTransformFollower is on the overlay stack, it still knows where the target is and any transformations that have been applied to it. + +<DashImage figure src="images/0FB5HpEeEvZ6NjJXl.webp" /> + + +Try it yourself using this [DartPad](https://dartpad.dev/58e5e12bb29370bbca75f19e2685387a) instance. The example code is as follows: + +Code by Hans Muller + +```dart +import 'dart:math' as math; + +import 'package:flutter/material.dart'; + +void main() { + runApp(MaterialApp(home: Slide())); +} + +class Indicator extends StatelessWidget { + Indicator({ Key key, this.link, this.offset }) : super(key: key); + + final LayerLink link; + final Offset offset; + + @override + Widget build(BuildContext context) { + return CompositedTransformFollower( + offset: offset, + link: link, + child: Container( + color: Colors.green, + ), + ); + } +} + +class Slide extends StatefulWidget { + Slide({ Key key }) : super(key: key); + + @override + _SlideState createState() => _SlideState(); +} + +class _SlideState extends State<Slide> { + final double indicatorWidth = 24.0; + final double indicatorHeight = 300.0; + final double slideHeight = 200.0; + final double slideWidth = 400.0; + + final LayerLink layerLink = LayerLink(); + OverlayEntry overlayEntry; + Offset indicatorOffset; + + Offset getIndicatorOffset(Offset dragOffset) { + final double x = (dragOffset.dx - (indicatorWidth / 2.0)).clamp(0.0, slideWidth - indicatorWidth); + final double y = (slideHeight - indicatorHeight) / 2.0; + return Offset(x, y); + } + + void showIndicator(DragStartDetails details) { + indicatorOffset = getIndicatorOffset(details.localPosition); + overlayEntry = OverlayEntry( + builder: (BuildContext context) { + return Positioned( + top: 0.0, + left: 0.0, + child: SizedBox( + width: indicatorWidth, + height: indicatorHeight, + child: Indicator( + offset: indicatorOffset, + link: layerLink + ), + ), + ); + }, + ); + Overlay.of(context).insert(overlayEntry); + } + + void updateIndicator(DragUpdateDetails details) { + indicatorOffset = getIndicatorOffset(details.localPosition); + overlayEntry.markNeedsBuild(); + } + + void hideIndicator(DragEndDetails details) { + overlayEntry.remove(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Overlay Indicator')), + body: Transform.rotate( + angle: -math.pi / 12.0, + child: Center( + child: CompositedTransformTarget( + link: layerLink, + child: Container( + width: slideWidth, + height: slideHeight, + color: Colors.blue.withOpacity(0.2), + child: GestureDetector( + onPanStart: showIndicator, + onPanUpdate: updateIndicator, + onPanEnd: hideIndicator, + ), + ), + ), + ), + ), + ); + } +} + +``` + +## Conclusion + +When implementing a UI that floats widgets on top of other widgets, using the overlay and linking the widgets together is a straightforward and powerful approach. In this article, you learned how to use a LayerLink to glue these widgets together. Now go forth and create your beautiful UI! + +*To learn more about Jose, visit his pages on [GitHub](https://github.com/JoseAlba), [LinkedIn](https://www.linkedin.com/in/josealba1996/), [YouTube](https://www.youtube.com/channel/UCOdKA_On0oPe1tz02z1QfxA?view_as=subscriber), and [Instagram](https://www.instagram.com/jose.alba/).* \ No newline at end of file diff --git a/sites/www/content/blog/how-to-watch-flutter-at-google-i-o-2018/images/1ZPr26vDyRE90NtHZJ6Jmgg.webp b/sites/www/content/blog/how-to-watch-flutter-at-google-i-o-2018/images/1ZPr26vDyRE90NtHZJ6Jmgg.webp new file mode 100644 index 0000000000..31559649aa Binary files /dev/null and b/sites/www/content/blog/how-to-watch-flutter-at-google-i-o-2018/images/1ZPr26vDyRE90NtHZJ6Jmgg.webp differ diff --git a/sites/www/content/blog/how-to-watch-flutter-at-google-i-o-2018/images/1dArkoJSnhvcjZ6p4ds63BA.webp b/sites/www/content/blog/how-to-watch-flutter-at-google-i-o-2018/images/1dArkoJSnhvcjZ6p4ds63BA.webp new file mode 100644 index 0000000000..baf7351ff2 Binary files /dev/null and b/sites/www/content/blog/how-to-watch-flutter-at-google-i-o-2018/images/1dArkoJSnhvcjZ6p4ds63BA.webp differ diff --git a/sites/www/content/blog/how-to-watch-flutter-at-google-i-o-2018/index.md b/sites/www/content/blog/how-to-watch-flutter-at-google-i-o-2018/index.md new file mode 100644 index 0000000000..5d6b69ad3b --- /dev/null +++ b/sites/www/content/blog/how-to-watch-flutter-at-google-i-o-2018/index.md @@ -0,0 +1,41 @@ +--- +title: "How to watch Flutter at Google I/O 2018" +description: "Google I/O 2018 is just a week away, and Flutter will be showing up in style at the event, with sessions, codelabs, office hours, an…" +publishDate: 2018-05-01 +author: aguinis +image: images/1dArkoJSnhvcjZ6p4ds63BA.webp +category: events +layout: blog +--- + +<DashImage figure src="images/1dArkoJSnhvcjZ6p4ds63BA.webp" /> + + +Google I/O 2018 is just a week away, and Flutter will be showing up in style at the event, with sessions, codelabs, office hours, an interactive sandbox space, and more. This is how to stay up to date with all-things Flutter from May 8–10th regardless of where you’ll be. + +To see the various Flutter-related sessions, visit [https://google.com/io/](https://events.google.com/io/) where you will be able to view each of the following talks online, both live and on-demand: + +* Tuesday, May 8 at 2:00 PM PDT —[ Customize Material Components for your product](https://events.google.com/io/schedule/?section=may-8&sid=247e7a44-c632-464e-954c-303ede5befd5&livestream=true&topic=flutter) + +* Tuesday, May 8 at 5:00 PM PDT — [Build great Material Design products across platforms](https://events.google.com/io/schedule/?section=may-8&sid=03c677fd-d082-4bf9-ae38-06829cfdada9&livestream=true&topic=flutter) + +* Wednesday, May 9 at 8:30 AM PDT — [Code beautiful UI with Flutter and Material Design.](https://events.google.com/io/schedule/?section=may-8&sid=086cd75d-8f7a-45d7-99bb-69dd3709535a&livestream=true&topic=flutter) + +* Wednesday, May 9 at 2:30 PM PDT — [Total mobile development made fun with Flutter and Firebase](https://events.google.com/io/schedule/?section=may-8&sid=94f05260-0dfd-4867-8d04-399e96595035&livestream=true&topic=flutter) + +* Thursday, May 10 at 10:30 AM PDT — [Build reactive mobile apps with Flutter](https://events.google.com/io/schedule/?section=may-8&sid=dab2bf45-6e44-4605-a997-9d446f95ef38&livestream=true&topic=flutter) + +* Thursday, May 10 at 3:30 PM PDT — [Add Firebase to your cross-platform React Native or Flutter app](https://events.google.com/io/schedule/?section=may-8&sid=c8374ad6-94f3-47bb-99fd-164c0d0a81bc&livestream=true&topic=flutter) + +The full Flutter livestream schedule can be [found here](https://events.google.com/io/schedule/?section=may-8&livestream=true&topic=flutter&utm_source=flutter&utm_medium=pre%20io%20announcement&utm_campaign=io18). Favorite these sessions on the website to find them easily later on. + +Also, be sure to check out a virtual tour of the Flutter Sandbox, which will be available on [g.co/io/guides](http://g.co/io/guides) by May 9. You will get to see what 2Dimensions has built for this! + +<DashImage figure src="images/1ZPr26vDyRE90NtHZJ6Jmgg.webp" /> + + +To get involved in discussions with other developers about Flutter and I/O, be sure to participate in one of the various global [I/O Extended events](http://google.com/io/extended) near you. If you’re interested in hosting your own viewing party, we’d [love to promote it](https://events.google.com/io/extended/form/). + +Anyone can get involved this year: at home, at the event, or at an I/O Extended gathering. Be sure to share your Flutter experiences with #Flutter on Twitter. + +See you next week! \ No newline at end of file diff --git a/sites/www/content/blog/how-to-write-a-flutter-web-plugin-part-2/images/1jLlh4b9zQ3u0aUA-hHAgKg.webp b/sites/www/content/blog/how-to-write-a-flutter-web-plugin-part-2/images/1jLlh4b9zQ3u0aUA-hHAgKg.webp new file mode 100644 index 0000000000..952dcf5d21 Binary files /dev/null and b/sites/www/content/blog/how-to-write-a-flutter-web-plugin-part-2/images/1jLlh4b9zQ3u0aUA-hHAgKg.webp differ diff --git a/sites/www/content/blog/how-to-write-a-flutter-web-plugin-part-2/index.md b/sites/www/content/blog/how-to-write-a-flutter-web-plugin-part-2/index.md new file mode 100644 index 0000000000..feeabb1201 --- /dev/null +++ b/sites/www/content/blog/how-to-write-a-flutter-web-plugin-part-2/index.md @@ -0,0 +1,341 @@ +--- +title: "How To Write a Flutter Web Plugin: Part 2" +description: "A guide for writing modern Flutter Web plugins" +publishDate: 2020-02-13 +author: harry_terkelsen +image: images/1jLlh4b9zQ3u0aUA-hHAgKg.webp +category: tutorial +layout: blog +--- + +## Introduction + +In [Part 1](https://medium.com/flutter/how-to-write-a-flutter-web-plugin-5e26c689ea1) of this guide, you learned how to add web support to a Flutter plugin using the same technologies that Android and iOS plugins use: `MethodChannel`s. While this is a viable method for adding web support to a Flutter plugin, it does have some disadvantages. + +For one, there is unnecessary overhead of sending plugin method calls over a `MethodChannel`. On the web, your entire app is compiled into one JavaScript bundle, so the plugin code is needlessly serializing the method call into a byte array, which is then instantly deserialized by the web plugin. + +Another disadvantage of using a `MethodChannel` is that it makes it difficult for the compiler to remove (by tree-shaking) unused plugin code. The web plugin calls the appropriate method based on the name of the method call passed by the `MethodChannel`, so the compiler has to assume that all of the methods in the plugin are live, and none of them can be tree-shaken out. + +In this article you will learn a different way to add web support for a Flutter plugin. If you plan to add web support to one of the plugins owned by the Flutter team (i.e., a plugin in the [`flutter/plugins`](https://github.com/flutter/plugins) or [`FirebaseExtended/flutterfire`](https://github.com/FirebaseExtended/flutterfire/) repo), then you *must* implement it this way. + +## Federated Plugins + +Before we walk through how to actually implement a plugin, it’s worth discussing how we structure multi-platform plugins on the Flutter team. We intend for the plugins owned by the Flutter team to be models showing best practices for implementing Flutter plugins. The main difference between the old way of writing Flutter plugins and the new way is that *platform-specific implementations are in different packages*. We call a plugin implemented in this way a *federated plugin*. + +## Advantages + +Why split the various implementations across multiple packages rather than combining them all into a single package? There are a few reasons why this is better for the long-term maintainability and growth of a plugin: + +* A plugin author does not need to have domain expertise for every supported Flutter platform (Android, iOS, Web, Mac OS, etc.). + +* You can add support for a new platform without the original plugin author needing to review and pull in your code. + +* Each package can be maintained and tested separately. + +Restructuring your plugin as a federated plugin allows anyone to implement support for new platforms without requiring you to do it yourself. For example, if Flutter supports Nintendo Switch in the future, then a Switch expert can add support for your plugin without you needing to learn all the new APIs. You can even vet the new Switch plugin, and if it meets your standards, you can make it an “endorsed implementation”, meaning that users of your plugin won’t even have to specifically depend on it in order to use it! + +## Platform Interfaces + +How can we implement web support for a plugin without using a `MethodChannel`? By creating an abstraction that describes exactly what the plugin package (such as `package:url_launcher`) requires from its platform-specific implementations (such as `package:url_launcher_web`). This approach abstracts *how* the plugin package communicates with the platform implementation and replaces it with a description of *what* behavior and data the plugin package requires from the platform. In the context of Flutter plugins, we call this abstraction a *platform interface*. + +## Example: `url_launcher` Platform Interface Sketch + +To give a concrete example of a platform interface, let’s look at a platform interface that could be defined for `package:url_launcher`. Our first web implementation of `url_launcher` set up a `MethodChannel` that listened for calls to the `launch` method, which took a `url` parameter. So, in order for a platform-specific backend to work with `package:url_launcher`, it needs to implement a method with the signature `Future<bool> launch(String url)`. A reasonable platform interface for `package:url_launcher` would look like this: + +```dart +abstract class UrlLauncherPlatform { + /// Launches the given [url]. Completes to [true] if the launch was successful. + Future<bool> launch(String url); +} +``` + +## Migrating a Plugin to Use a Platform Interface + +In the [`flutter/plugins`](https://github.com/flutter/plugins) GitHub repository, we have adopted a style for writing federated plugins with platform interfaces. You must emulate this style if you want to land a Pull Request adding new platform support to one of the officially-supported plugins. Migrating a plugin to the new federated platform interface format is done in 3 steps (3 PRs): + +1. Add the `<plugin name>_platform_interface` package. + +1. Migrate the plugin to use the platform interface. + +1. Add a `<plugin name>_web` package that extends the platform interface. + +Let’s work through an example of how this would be done for a real plugin. + +## Example: Migrating package:url_launcher + +<DashImage figure src="images/1jLlh4b9zQ3u0aUA-hHAgKg.webp" /> + + +## Step 1: Creating the platform interface package + +In the first step, we’ll create the platform interface package and rearrange the existing code to use our federated plugin directory layout. For the purposes of this example, we are assuming that the plugin is in a repo that is laid out like the [`flutter/plugins`](https://github.com/flutter/plugins) GitHub repo (in other words, the plugin lives in a directory like `packages/url_launcher`). Specifically, we are assuming a layout that looks like this: + +``` +- README.md +- packages/ + - some_other_plugin/ + … + - url_launcher/ + - pubspec.yaml + - lib/ + … + - android/ + … + - ios/ + … +``` + + +The gist of this refactoring is that we are creating a directory that holds not only the plugin package, but also the platform interface package and the web package. We want to move `packages/url_launcher` to `packages/url_launcher/url_launcher` and create another package `packages/url_launcher/url_launcher_platform_interface` (and eventually create another package `packages/url_launcher/url_launcher_web`). + +## Move `url_launcher` to its own subdirectory + +Assuming you’re in the `packages/` directory, you can move the `url_launcher` plugin to a federated subdirectory by running the following: + +``` +$ git mv url_launcher url_launcher_tmp +$ mkdir url_launcher +$ git mv url_launcher_tmp url_launcher/url_launcher +$ git commit -m "Move url_launcher to url_launcher/url_launcher" +``` + + +## Create the url_launcher_platform_interface package + +Move to the `packages/url_launcher` directory we created in the last step. Then create the platform interface package by running: + +``` +$ mkdir url_launcher_platform_interface +``` + + +Now, you need to add a few files in the `url_launcher_platform_interface` to make it a real package. For the license file, you can usually `git cp` the `LICENSE` from the “plugin package” (in this case `package:url_launcher`). You should create a `CHANGELOG.md` file that contains the following: + +``` +## 1.0.0 + +- Initial open-source release. +``` + + +You also need to define a `pubspec.yaml`; you can use the [`pubspec.yaml`](https://github.com/flutter/plugins/blob/master/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml) for the actual `package:url_launcher_platform_interface` as a template. The note about avoiding breaking changes should be included in the `pubspec.yaml` of every platform interface package. + +Finally, you also need a `README.md` file. Again, you can use the [`README.md`](https://github.com/flutter/plugins/blob/master/packages/url_launcher/url_launcher_platform_interface/README.md) in the actual `package:url_launcher_platform_interface` as a template. + +Let’s get to the actual code. Edit the file `lib/url_launcher_platform_interface.dart` and paste in the following code: + +```dart +import 'dart:async'; + +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'method_channel_url_launcher.dart'; + +/// The interface that implementations of url_launcher must implement. +/// +/// Platform implementations should extend this class rather than implement it as `url_launcher` +/// does not consider newly added methods to be breaking changes. Extending this class +/// (using `extends`) ensures that the subclass will get the default implementation, while +/// platform implementations that `implements` this interface will be broken by newly added +/// [UrlLauncherPlatform] methods. +abstract class UrlLauncherPlatform extends PlatformInterface { + /// Constructs a UrlLauncherPlatform. + UrlLauncherPlatform() : super(token: _token); + + static final Object _token = Object(); + + static UrlLauncherPlatform _instance = MethodChannelUrlLauncher(); + + /// The default instance of [UrlLauncherPlatform] to use. + /// + /// Defaults to [MethodChannelUrlLauncher]. + static UrlLauncherPlatform get instance => _instance; + + /// Platform-specific plugins should set this with their own platform-specific + /// class that extends [UrlLauncherPlatform] when they register themselves. + static set instance(UrlLauncherPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + /// Launches the given [url]. Completes to [true] if the launch was successful. + Future<bool> launch(String url) { + throw UnimplementedError('launch() has not been implemented.'); + } +} +``` + +In the code comments above, once again implementers of platform implementations are told to use `extends` rather than `implements` to write their implementations of `UrlLauncherPlatform`. This is the same warning as in the `pubspec.yaml` and `README.md` files. Not only does the class warn implementers to use `extends` rather than `implements`, it enforces this by extending `PlatformInterface`. If you’re interested in seeing how `PlatformInterface` enforces that subtypes use `extends` rather than `implements`, check out [`package:plugin_platform_interface`](https://pub.dev/packages/plugin_platform_interface). The main thing you should take away from the code above is that you must have the same boilerplate in your platform interface classes in order to prevent implementers from using `implements`. + +Notice also the `instance` getter and setter in the platform interface. New platforms can support a plugin by `extend`ing the platform interface and setting their platform-specific class as the default instance. The default instance for any platform interface should always be one that uses `MethodChannel` to send the method call on the channel to the platform backend. Defaulting to an implementation backed by a `MethodChannel` means that the existing Android and iOS implementations will continue to work by default (as well as web implementations if they were implemented using `MethodChannel` as shown in [Part 1](https://medium.com/flutter/how-to-write-a-flutter-web-plugin-5e26c689ea1) of this guide). + +The last thing to note is that all of the methods in the platform interface should have a default implementation that just throws an `UnimplementedError`. Since every implementation of `UrlLauncherPlatform` must use `extends`, then if a new method is added to the interface, it won’t be a breaking change that causes apps to break. If an implementation used `implements`, it would be a breaking change because that implementation would no longer implement every method in the interface. + +### Creating `method_channel_url_launcher.dart` + +We set the default `UrlLauncherPlatform` to `MethodChannelUrlLauncher` in the last step. Now we need to write `MethodChannelUrlLauncher`. Edit `lib/method_channel_url_launcher.dart` and paste in the following: + +```dart +import 'dart:async'; + +import 'package:flutter/services.dart'; + +import 'url_launcher_platform_interface.dart'; + +const MethodChannel _channel = MethodChannel('plugins.flutter.io/url_launcher'); + +/// An implementation of [UrlLauncherPlatform] that uses method channels. +class MethodChannelUrlLauncher extends UrlLauncherPlatform { + @override + Future<bool> launch(String url) { + return _channel.invokeMethod<bool>( + 'launch', + <String, Object>{ + 'url': url, + }, + ); + } +} +``` + +If you followed [Part 1](https://medium.com/flutter/how-to-write-a-flutter-web-plugin-5e26c689ea1) of this guide, you will recognize the code to invoke the method on the `MethodChannel` above. In the next step, when we refactor `package:url_launcher` to use `package:url_launcher_platform_interface`, since the default platform interface uses `MethodChannel` to dispatch the calls, all currently existing platforms for `package:url_launcher` should continue to work. + +### Finishing package:url_launcher_platform_interface + +With this code written, we are done with `package:url_launcher_platform_interface`. All that’s left is to commit the new code, submit it to version control (e.g. land a PR on GitHub), and upload the new package to pub.dev. You must submit this package to pub.dev before moving on to the next step because we will be refactoring `package:url_launcher` to have a dependency on this new package. + +## Step 2: Refactoring package:url_launcher to use the platform interface + +Now that our platform interface package from Step 1 has been published to pub.dev, let’s use it in `package:url_launcher`. + +## Updating dependencies + +In the `pubspec.yaml` for `package:url_launcher`, add a dependency on `url_launcher_platform_interface` as shown below: + +``` +name: url_launcher +version: <bump to next minor version> +… +dependencies: + flutter: + sdk: flutter + url_launcher_platform_interface: ^1.0.0 + … +``` + + +## Refactoring all usages of MethodChannel + +If you recall from [Part 1](https://medium.com/flutter/how-to-write-a-flutter-web-plugin-5e26c689ea1) of this guide, our (simplified) `package:url_launcher` contained the following `launch()` method: + +```dart +const MethodChannel _channel = MethodChannel('plugins.flutter.io/url_launcher'); + +Future<bool> launch(String urlString) async { + assert(urlString != null); + final bool result = await _channel.invokeMethod<bool>( + 'launch', + <String, Object>{ + 'url': urlString, + }, + ); + ... + return result; +} +``` + +We need to refactor this to use the platform interface we just defined instead. Change it to the following: + +```dart +Future<bool> launch(String urlString) async { + assert(urlString != null); + final bool result = await UrlLauncherPlatform.instance.launch(urlString); + ... + return result; +} +``` + +As we covered in the previous section, since the default `UrlLauncherPlatform` uses `MethodChannel`, this refactoring is safe. We aren’t changing behavior with this change, just paving the way for platforms to implement `package:url_launcher` without using method channels. + +Make sure to add an entry to the `CHANGELOG.md` saying that you are migrating `package:url_launcher` to use the platform interface. With this small refactoring done, you can commit your changes, upload `package:url_launcher` to pub.dev, and we can move on to writing the implementation of `package:url_launcher` for the web platform. + +## Step 3: Implementing package:url_launcher_web using the platform interface + +In [Part 1](https://medium.com/flutter/how-to-write-a-flutter-web-plugin-5e26c689ea1) of this guide, we created a `package:url_launcher_web` that uses `MethodChannel` for communication with the plugin. Let’s refactor this plugin to use the platform interface instead. + +Replace the contents of `lib/url_launcher_web.dart` with the following: + +```dart +import 'dart:async'; +import 'dart:html' as html; + +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'package:meta/meta.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +/// The web implementation of [UrlLauncherPlatform]. +/// +/// This class implements the `package:url_launcher` functionality for the web. +class UrlLauncherPlugin extends UrlLauncherPlatform { + /// Registers this class as the default instance of [UrlLauncherPlatform]. + static void registerWith(Registrar registrar) { + UrlLauncherPlatform.instance = UrlLauncherPlugin(); + } + + @override + Future<bool> launch(String url) { + return Future<bool>.value(html.window.open(url, '') != null); + } +} +``` + +Note how we have changed `UrlLauncherPlugin` to `extend UrlLauncherPlatform`. We still have the `registerWith()` method from before, but now instead of registering a `MethodChannel`, we instead register that this is the default instance of `UrlLauncherPlatform`. Now, in `package:url_launcher` when it calls `UrlLauncherPlatform.instance.launch()` it will call the `launch()` method we define here. + +We touched on the advantages of using a platform interface above, but here we can see it more clearly. Note how we are no longer serializing and deserializing every method call to the plugin, we are simply calling a method. Writing platform-specific implementations for a plugin no longer requires understanding Flutter’s `MethodChannel` APIs, it is clear that this class is implementing all the necessary functionality to get `package:url_launcher` working on the web. + +## Platform Interface Design Principles + +The example above is very simple; the platform interface has only one method. In this section we’ll cover some general principles to keep in mind when creating a platform interface package for a plugin. If you keep these principles in mind, creating a platform interface package for a plugin is straightforward in almost all cases. + +## Make a 1:1 mapping with MethodChannel calls + +In the ideal case, you can design a platform interface for a plugin by finding everywhere the plugin calls the `MethodChannel` and making a method in the platform interface corresponding to each call. That’s what we did in the `package:url_launcher` example above. This makes it very simple to implement the default platform interface implementation backed by a `MethodChannel` since each method in the platform interface corresponds exactly to a `MethodChannel` call. This also makes it trivial to refactor the plugin package to use the platform interface: simply replace each `MethodChannel` call with the corresponding call on the platform interface. + +## Keep the platform interface package minimal + +Avoid bringing abstractions from the plugin package into the platform interface package. This allows the plugin package to be more flexible. + +To give an example, suppose that instead of a `launch()` method, the `package:url_launcher` interface looked like this: + +```dart +class UrlLauncher { + final String url; + const UrlLauncher(this.url); + + Future<bool> launch() { + // Contains a method channel call to launch the url + } +} +``` + +Suppose further that we mimicked this API in the platform interface, resulting in something like: + +```dart +class UrlLauncherPlatform { + Launcher createLauncher(String url); +} + +class Launcher { + Future<bool> launch(); +} +``` + +Now we won’t be able to refactor `package:url_launcher` to a different API without also refactoring `package:url_launcher_platform_interface`. If `package:url_launcher_platform_interface` followed the advice above and just had a method for each `MethodChannel` call, then the fake API for `package:url_launcher` above and the actual API will both be simple to implement via the platform implementation. + +## Enforce that implementers use extends + +We’ve mentioned it a few times already in this article, but it is a very important point that bears repeating. Make sure you use `package:plugin_platform_interface` to enforce that implementers of your platform interface use `extends` rather than `implements`. Read more about implementing [federated plugins](https://docs.google.com/document/d/1LD7QjmzJZLCopUrFAAE98wOUQpjmguyGTN2wd_89Srs/edit#heading=h.xabf9lffxrkb) for background information on why `implements` is a bad idea for platform interfaces. + +## Conclusion + +Whether you are writing a new plugin or adding web support to an existing one, using a platform interface will future-proof your plugin and make it much easier to extend and maintain in the long run. In this article you learned how to refactor your plugin to use a platform interface, and how to write a web plugin using that interface. Now go forth and write your plugins! \ No newline at end of file diff --git a/sites/www/content/blog/how-to-write-a-flutter-web-plugin/images/1UTowUOvpkFW35rcuJ3Lf1w.webp b/sites/www/content/blog/how-to-write-a-flutter-web-plugin/images/1UTowUOvpkFW35rcuJ3Lf1w.webp new file mode 100644 index 0000000000..13381857e3 Binary files /dev/null and b/sites/www/content/blog/how-to-write-a-flutter-web-plugin/images/1UTowUOvpkFW35rcuJ3Lf1w.webp differ diff --git a/sites/www/content/blog/how-to-write-a-flutter-web-plugin/index.md b/sites/www/content/blog/how-to-write-a-flutter-web-plugin/index.md new file mode 100644 index 0000000000..54f758b86b --- /dev/null +++ b/sites/www/content/blog/how-to-write-a-flutter-web-plugin/index.md @@ -0,0 +1,253 @@ +--- +title: "How to Write a Flutter Web Plugin" +description: "Introduction" +publishDate: 2019-11-20 +author: harry_terkelsen +image: images/1UTowUOvpkFW35rcuJ3Lf1w.webp +category: tutorial +layout: blog +--- + +Do you want to add support for a Flutter plugin? This article will show you how web support was added to the [url_launcher](https://pub.dev/packages/url_launcher) plugin. Before you read this article, you should already be familiar with plugin development and the concepts in [Developing plugin packages](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin). + +## Example: package:url_launcher + +First, let’s create an example app that uses `package:url_launcher`, so you can verify that it works correctly. + +**Setup** + +In order to use web plugins, you need to be on the Flutter dev channel. Make sure that you’re on the dev channel by running: + +``` +$ flutter channel dev +$ flutter upgrade +``` + + +Now, you need to enable web support so that Flutter can set up your app to run on the web: + +``` +$ flutter config --enable-web +``` + + +Now, create a directory named `url_launcher_example`(let’s assume `~/url_launcher_example`), and create a Flutter project in it: + +``` +$ mkdir “~/url_launcher_example” +$ cd “~/url_launcher_example” +$ flutter create . +``` + + +**Use `package:url_launcher`** + +Our example app will have just a button that launches google.com. + +First, update the pubspec so that you depend on `package:url_launcher`. In `pubspec.yaml`, under `dependencies`, add the line (highlighted in bold): + +``` +name: url_launcher_example + ... +dependencies: + flutter: + sdk: flutter + **url_launcher: 5.2.3** + ... +``` + + +Now, replace the entire contents of `lib/main.dart` with the following: + +```dart +import 'package:url_launcher/url_launcher.dart'; +import 'package:flutter/material.dart'; + +void main() => runApp(UrlLauncherExample()); + +class UrlLauncherExample extends StatelessWidget { + static const String _title = 'URL Launcher Example'; + @override + Widget build(BuildContext context) { + return MaterialApp( + title: _title, + home: Scaffold( + appBar: AppBar(title: const Text(_title)), + body: LaunchButton(), + ), + ); + } +} + +class LaunchButton extends StatelessWidget { + LaunchButton({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Center( + child: RaisedButton( + onPressed: _launch, + child: Text( + 'Launch!', + style: TextStyle(fontSize: 20), + ), + ), + ); + } + + void _launch() { + launch('https://www.google.com'); + } +} + +``` + +Verify that the app works by running it on your Android or iOS device, or simulator by running the app normally with `flutter run`. The app should look like this screenshot. Try clicking the **Launch**! button and verify that it opens Google. + +<DashImage figure src="images/1UTowUOvpkFW35rcuJ3Lf1w.webp" alt="URL Launcher Example app" caption="URL Launcher Example app" /> + + +Now, you can run the same app on the web with `flutter run -d chrome`. The app should open and render just like the mobile version, but clicking **Launch**! does nothing. Let’s start writing the web plugin for `url_launcher`. + +## **Creating `package:url_launcher_web`** + +Create a new directory called `url_launcher_web` (let’s assume `~/url_launcher_web`): + +``` +$ mkdir “~/url_launcher_web” +$ cd “~/url_launcher_web” +``` + + +Unfortunately, there is no template for web plugins currently (that is, you can’t use `flutter create`), so you’ll have to create the plugin manually. But, before you start coding, some discussion is in order about how this is actually going to be implemented. + +**Looking into `package:url_launcher`** + +Let’s take a look at how `package:url_launcher` is implemented, so you know what to do for `package:url_launcher_web`. The main code can be found [here](https://github.com/flutter/plugins/blob/f04b3c8565ca9d2dac1f7f1ee8a44f41f97fe542/packages/url_launcher/url_launcher/lib/url_launcher.dart). These are the main bits you should care about: + +```dart +const MethodChannel _channel = MethodChannel('plugins.flutter.io/url_launcher'); + +Future<bool> launch( + String urlString, { + ... +}) async { + assert(urlString != null); + final bool result = await _channel.invokeMethod<bool>( + 'launch', + <String, Object>{ + 'url': urlString, + ... + }, + ); + ... + return result; +} +``` + +Almost all Flutter plugins are written like this. They create a `MethodChannel`, and then the plugin works by sending a method call on the channel to the “platform-side” (that is, Android or iOS) of the plugin. So, the way this plugin works on Android is that there is some code, written in Java, that registers a `MethodChannel` on the Android side. The `MethodChannel` waits for method calls, which call the required Android code to launch a URL. In order to get this plugin working on the web, you need to do the same thing as in the Android and iOS implementations, create a `MethodChannel` that waits for method calls, and when the `MethodChannel` receives them, launches the given URL. + +**Implementing `package:url_launcher_web`** + +Web implementations of Flutter plugins are written as Dart packages. Let’s begin with the `pubspec.yaml`. Assuming you’re in your `url_launcher_web` directory you created earlier, create a file named `pubspec.yaml`, and paste the following code in the file: + +```yaml +name: url_launcher_web +version: 0.0.1 + +flutter: + plugin: + platforms: + web: + pluginClass: UrlLauncherPlugin + fileName: url_launcher_web.dart + +dependencies: + flutter: + sdk: flutter + flutter_web_plugins: + sdk: flutter + +environment: + sdk: ">=2.0.0-dev.28.0 <3.0.0" + flutter: ">=1.5.0 <2.0.0" +``` + +Some key things to note: + +* The `platforms:` section contains a section for `web:` that declares the name of the class where you will implement the plugin, as well as the filename containing the class. + +* The `flutter_web_plugins` dependency lets you register the web implementation of `url_launcher`. + +We declared that our implementation will be a class named `UrlLauncherPlugin` and be written in `url_launcher_web.dart`, so let’s write that class now. Make sure you create a `lib/` directory first, then edit `lib/url_launcher_web.dart`, and paste the following code in the file: + +```dart +import 'dart:async'; +import 'dart:html' as html; + +import 'package:flutter/services.dart'; +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; + +class UrlLauncherPlugin { + static void registerWith(Registrar registrar) { + final MethodChannel channel = MethodChannel( + 'plugins.flutter.io/url_launcher', + const StandardMethodCodec(), + registrar.messenger); + final UrlLauncherPlugin instance = UrlLauncherPlugin(); + channel.setMethodCallHandler(instance.handleMethodCall); + } + + Future<dynamic> handleMethodCall(MethodCall call) async { + switch (call.method) { + case 'launch': + final String url = call.arguments['url']; + return _launch(url); + default: + throw PlatformException( + code: 'Unimplemented', + details: "The url_launcher plugin for web doesn't implement " + "the method '${call.method}'"); + } + } + + + bool _launch(String url) { + return html.window.open(url, '') != null; + } +} +``` + +There are several key points to note in our implementation, let’s go over them one by one. + +**Registering the plugin with `registerWith`** + +Just as on Android or iOS, web plugins need to do some initialization before the app runs. This is done in the static `registerWith` method, which takes a `Registrar` (which comes from `package:flutter_web_plugins`). + +In this case, we are registering a `MethodChannel` to listen for incoming messages from the app. Note how we initialize the `MethodChannel` with `registrar.messenger`. All `MethodChannel`s have a `BinaryMessenger` that they use to send and receive messages. By default, a `MethodChannel` uses the default `BinaryMessenger` defined in Flutter. The `BinaryMessenger` sends messages from the app to the platform side, but we are writing a plugin that is on the platform side and should receive messages from the app, so we need to initialize the `MethodChannel` with a different `BinaryMessenger`. Luckily, the `Registrar` that is passed to the plugin in `registerWith` has a messenger that does the right thing. By initializing our `MethodChannel` with it, we now have a `MethodChannel` that receives method calls from the app. + +**Handling method calls** + +The `MethodChannel` we created registered `handleMethodCall` as its method call handler. This means that any time the app-side `MethodChannel` (the one created in `package:url_launcher`) sends a method call to the platform-side `MethodChannel` (the one we created in `registerWith`) this method call handler is invoked. + +If the handler receives a method call to the `‘launch’` method, then it invokes `_launch`, which simply opens a new window with the given URL. + +## Using the web plugin in the example app + +Our web plugin is looking good! Now, we need to use it from the example app we created earlier. + +Modify the pubspec in `~/url_launcher_example` to add a dependency to `~/url_launcher_web`. Your change should look something like this. (Make sure to use the correct path for where you actually put your directories): + +``` +dependencies: + flutter: + sdk: flutter + url_launcher: 5.2.3 + **url_launcher_web: + path: ../url_launcher_web** +``` + + +Now, run the example app again with `flutter run -d chrome`. Try clicking the **Launch**! button. It should open google.com in a new tab. + +Congratulations! You successfully added web support to a Flutter plugin! \ No newline at end of file diff --git a/sites/www/content/blog/how-we-built-a-flutter-powered-ai-coffee-shop/images/18z1hY0jGVYmpLVpG_iZ4Dg.webp b/sites/www/content/blog/how-we-built-a-flutter-powered-ai-coffee-shop/images/18z1hY0jGVYmpLVpG_iZ4Dg.webp new file mode 100644 index 0000000000..840d1ec5d2 Binary files /dev/null and b/sites/www/content/blog/how-we-built-a-flutter-powered-ai-coffee-shop/images/18z1hY0jGVYmpLVpG_iZ4Dg.webp differ diff --git a/sites/www/content/blog/how-we-built-a-flutter-powered-ai-coffee-shop/images/1BkccCrU04OtaehRtGmMlEA.webp b/sites/www/content/blog/how-we-built-a-flutter-powered-ai-coffee-shop/images/1BkccCrU04OtaehRtGmMlEA.webp new file mode 100644 index 0000000000..3127f6240c Binary files /dev/null and b/sites/www/content/blog/how-we-built-a-flutter-powered-ai-coffee-shop/images/1BkccCrU04OtaehRtGmMlEA.webp differ diff --git a/sites/www/content/blog/how-we-built-a-flutter-powered-ai-coffee-shop/images/1mQmkXnT5Dzc26Sm7e_RK5g.webp b/sites/www/content/blog/how-we-built-a-flutter-powered-ai-coffee-shop/images/1mQmkXnT5Dzc26Sm7e_RK5g.webp new file mode 100644 index 0000000000..4e2e8add7f Binary files /dev/null and b/sites/www/content/blog/how-we-built-a-flutter-powered-ai-coffee-shop/images/1mQmkXnT5Dzc26Sm7e_RK5g.webp differ diff --git a/sites/www/content/blog/how-we-built-a-flutter-powered-ai-coffee-shop/images/1o8sb5en4Yhr9J0R1cMMO_g.jpeg b/sites/www/content/blog/how-we-built-a-flutter-powered-ai-coffee-shop/images/1o8sb5en4Yhr9J0R1cMMO_g.jpeg new file mode 100644 index 0000000000..38a0a59f37 Binary files /dev/null and b/sites/www/content/blog/how-we-built-a-flutter-powered-ai-coffee-shop/images/1o8sb5en4Yhr9J0R1cMMO_g.jpeg differ diff --git a/sites/www/content/blog/how-we-built-a-flutter-powered-ai-coffee-shop/index.md b/sites/www/content/blog/how-we-built-a-flutter-powered-ai-coffee-shop/index.md new file mode 100644 index 0000000000..3b942d32a9 --- /dev/null +++ b/sites/www/content/blog/how-we-built-a-flutter-powered-ai-coffee-shop/index.md @@ -0,0 +1,123 @@ +--- +title: "How we built a Flutter-powered AI coffee shop" +description: "If you start giving away coffee, people will show up. The Flutter team had been trying for a while to find a way to put this principle to…" +publishDate: 2026-06-22 +author: craiglabenz +image: images/1o8sb5en4Yhr9J0R1cMMO_g.jpeg +layout: blog +--- + +<DashImage figure src="images/1o8sb5en4Yhr9J0R1cMMO_g.jpeg" alt="Dash sits on a coffee shop’s checkout counter drinking a latte with the Flutter logo printed on top" caption="Dash sits on a coffee shop’s checkout counter drinking a latte with the Flutter logo printed on top" /> + + +If you start giving away coffee, people will show up. The Flutter team had been trying for a while to find a way to put this principle to work with a demo that started with a Flutter app and ended with a hot, tasty beverage. The problem was how to make it more interesting, more personal than just taking orders and delivering. Coffee’s cool, but it’s not *The One Thing You Definitely Remember from Google I/O* cool. + +So what changed? Two new bits of tech arrived on scene. One is Nano Banana, with its ability to quickly generate customized images. The second is GenUI, with its ability to generate UX dynamically at runtime and offer attendees personally tailored questions about how they’d like to modify those images. Put the two together with Flutter and you can offer folks the chance to order coffee with an image of their happy place printed on top with an inkjet printer. Now *that’s* pretty memorable. + +All we had to do was execute. Twice, as it turned out — the demo was called “GenLatte” at Cloud Next and “Antigravity Coffee Co.” at Google I/O. For simplicity and to save a few tokens for the agent summarizing this article for you, the name “GenLatte” is used below. + +## We built a coffee shop + +Realizing a vision as ambitious as GenLatte involves a lot of people. Like, *a lot of people*. There are teams to spec out the physical space and decide how “customers” [1] will flow. There are branding and concept teams to create the aesthetic. There are of course construction teams to go to the hardware store and buy lumber. Eventually, there’s also the software engineers slapping code together to build the various apps, which is where I happened to come in. This blog post will only outline the software, but that is not to minimize the incredible work that went into the many other layers required for GenLatte to eventually open its doors. + +[1] Are they still customers if you don’t charge anything? + +## How GenLatte works + +At its core, GenLatte is a Flutter app with a Firebase backend. + +Zooming in one step further, it’s a monorepo with separate Flutter, Firebase, and shared code projects. Using a monorepo allows us to deduplicate dependencies, share business logic, and execute atomic deployments. The Flutter app also runs on iOS and Android, but customers only ever interacted with the web version. We used Cloud Build and Firebase Hosting to keep our deployment story from being *Someone runs $firebase deploy on their laptop.* On the server, we have a mix of trigger functions and HTTP callables, giving the app flexibility between explicit data flows and reactive data management. + +Within the Flutter app itself, we built 5 separate UIs: the kiosk screen where sleepy conference attendees order a coffee, a barista screen to show fresh orders, a moderator screen to verify the safety of each submission, a large queue screen to give customers a sense of when to expect their drink, and a purely whimsical “recent orders” screen to show the last 13 orders as floating bubbles and allow everyone a final chance to appreciate their digital creation. + +<YoutubeEmbed id="taWr5k-o6eY" title="GenLatte ordering and checkout flow" fullwidth="true"/> + + +<YoutubeEmbed id="GbbGtYKQvaY" title="GenLatte moderator's order management" fullwidth="true"/> + + +<YoutubeEmbed id="3Kd6WwcPM5M" title="GenLatte barista's order management" fullwidth="true"/> + + +<YoutubeEmbed id="_2kbagKgUPM" title="GenLatte's whimsical recent orders screen" fullwidth="true"/> + + +## Demo-grade security + +One of Google’s principal concerns in any demo featuring user-generated content is preventing volunteers from graciously donating their red-teaming expertise to publish inappropriate content for public consumption. In practice, this materializes into several distinct concerns: + +* **Preventing authorized users from creating NSFW content:** To prevent this, GenLatte implemented a three-tiered approach. First, raw “happy place” strings were sent to Gemini Flash for quick moderation, along with thirty or so examples outlining which unsavory topics we wanted to block. Then, once a happy place was approved, we started generating images and relied on Nano Banana’s own internal training to only produce SFW content. At the end of the process, our human moderator would manually approve each image. With this all in place, we were quite confident that GenLatte wouldn’t embarrass Google. + +* **Preventing the spoofing of admin accounts:** Another way authorized users can potentially break your app is to visit screens that grant elevated permissions you did not intend to share. Given GenLatte’s back-of-house moderator and barista screens, it obviously ran this risk. Our choice to deploy to the web, where users can arbitrarily navigate using the browser URL bar, further increased pressure. To solve this, our GoRouter implementation features centralized, [declarative routing checks](https://github.com/flutter/demos/blob/main/genlatte/genlatte-ui/lib/src/core/routing/redirection.dart) to ensure that every page visit is legal for the given account, as determined by Firebase custom claims. + +* **Preventing unauthorized writes to Firestore:** Authorized users using the app is one thing, but someone reading the contents of `firebase_options.dart`, starting a new project, and seeing what havoc they can wreak, is another. To prevent this, we tied all reads and writes to strict account credentials with tight security rules, which you can [use as inspiration for your own projects](https://github.com/flutter/demos/blob/main/genlatte/firebase/firestore.rules). + +## Delivering fun images + +Once we convinced ourselves that our app wouldn’t be hacked on the demo stage floor, we got to making sure that Gemini could deliver consistent images that would look great on coffee. This portion of the project might have involved gratuitously over-caffeinated afternoons at the office, so it’s probably no surprise that we zipped through it. + +At its core, our system involved upscaling the user’s happy place, a prompt capped at 50 characters but often no more than a word or two, into four unique and highly specific image requests. For example, for a user who entered a happy place of “at home with my family”, GenLatte might ask Nano Banana for an image of “A quaint home in the woods with a fire in the fireplace and dinner waiting on the table. The home is full of family and friends, all celebrating and enjoying each other’s company. The lighting and mood is…” (so on and so forth). Gemini handled this upscaling, meaning the four prompts were all different; delivering the variety of images that users saw within the app. + +We also optimized Nano Banana for pleasing compositions; specifically, “a leading S curve toward the subject”, which rotated between roads, rivers, hills, coastlines, or other abstractions. This prompting trick greatly increased Nano Banana’s percentage of appealing, usable images. + +<DashImage figure src="images/1mQmkXnT5Dzc26Sm7e_RK5g.webp" alt="Sample Nano Banana image: a puppy on a trail." caption="Sample Nano Banana image: a puppy on a trail." /> + + +<DashImage figure src="images/1BkccCrU04OtaehRtGmMlEA.webp" alt="Sample Nano Banana image: a compass on a trail." caption="Sample Nano Banana image: a compass on a trail." /> + + +<DashImage figure src="images/18z1hY0jGVYmpLVpG_iZ4Dg.webp" alt="Sample Nano Banana image: a whimsical forest." caption="Sample Nano Banana image: a whimsical forest." /> + + +For GenLatte’s second appearance, as Antigravity Coffee Co, at I/O in Mountain View this May, we updated our prompts to rotate through different suggested compositions. This kept things fresh for both event staff seeing hundreds of images per day and individual guests who simply wanted an evocative image for *their* coffee. + +## Modifying the images in real time + +GenLatte’s true magic didn’t just come from whipping up an image and printing it on to coffee; but from specifically dialing it in to the person’s exact tastes. Behind that Tweak button were four questions, pre-written by Gemini, for how the image might be modified. + +<YoutubeEmbed id="b2yH9VXT1Dw" title="" fullwidth="true"/> + + +These images were derived from the original, upscaled prompt itself; so returning to our cozy family happy place from earlier, Gemini might have provided questions about what type of food was on the table, whether there were more or fewer people, or whether the celebration was for any specific life event. The other “at home with my family” happy places the user saw might have gone in different directions, and so tweaking those selections would have surfaced different questions appropriate for those images. + +Once the user’s answers were in, we went back to Nano Banana with the same prompt, submitted multiple times concurrently, to get new art. The prompt looked something like this: + +``` +After seeing an image derived from this original prompt: + +{{original_prompt}} + +The user requested these changes: + +{{questions[0].body}}: {{questions[0].answer}} +{{questions[1].body}}: {{questions[1].answer}} +{{questions[2].body}}: {{questions[2].answer}} +{{questions[3].body}}: {{questions[3].answer}} + +Please generate this new image. +``` + + +From Las Vegas to Mountain View, it was magical for us, after months of building GenLatte, to see people’s faces light up at their modified images! + +## A2UI and GenLatte + +GenLatte is not just a Flutter and Firebase demo, or even a generative AI demo; but a *GenUI* demo. GenUI can come in many different forms, but for GenLatte, it connected the dots between showing users the correct user interface to answer one of Gemini’s questions. The key detail is that not all questions are best served by the same UI. A question like “Is your family celebrating a specific life event?” is open-ended, and so requires a text field; but “What time of day is this?” probably only has a few relevant options, and so is best served by a radio button. + +There are a range of ways to implement GenUI in an app. On the conservative end are tightly controlled, on-rails experiences where the agent chooses from a coarse-grain widget catalog to render predictable UIs. More adventurous apps can give their agents more freedom, composing UIs from finer-grain catalogs to render just about anything imaginable. For GenLatte, we landed on the conservative end: exposing a small catalog of branded widgets that the agent could use to display questions. That gave Gemini the ability to choose the right one for the moment while making sure the overall composition stayed within our design guidelines. + +If you’d like to see what Gemini can do with more freedom, check out [Hatcha](https://github.com/gskinnerteam/flutter-hatcha-app), a party-planning app. It goes much further into GenUI by delegating significantly more runtime responsibility to Gemini. + +## Three thousand coffees later… + +After serving 1,200 lattes at Google Cloud Next and another 1,800 at Google I/O, it’s safe to say that GenLatte was a success. I’ve scarcely been more caffeinated than I was while building this demo or staffing it at both events, nor have I seen more delighted smiles on faces immediately after using an app. + +<YoutubeEmbed id="DHWiny5bjJc" title="The magic behind GenLatte: Flutter, Firebase, and Gemini" fullwidth="true"/> + + +Flutter made the whimsical UI a delight to build; from its elegant support for dynamic layouts and custom shaders, to its ability to seamlessly target multiple platforms. Firebase made the backend a breeze: handling deployment and scaling headaches for us and offering reactive, full-stack data binding. Gemini and Nano Banana (of course) turned our users’ happy place prompts into fantastical art that was a delight to see take physical form. And of course, the true hero of the operation, our baristas, made excellent lattes — because the whole thing would have been a flop if the coffee wasn’t any good! +> To read GenLatte’s code, visit its [folder in the flutter/demos repository](https://github.com/flutter/demos/tree/main/genlatte). However, be warned, code in that repository is unmaintained and offered for inspirational purposes only. + +We’re ecstatic about the future of app development in 2026. With tools like Flutter, Firebase, and Gemini; the scope of experiences you can realistically deliver your users has grown exponentially. + +To get started with GenUI yourself, visit [Flutter’s documentation](https://docs.flutter.dev/ai/genui). Let us know if you have an idea for how you might use these patterns in your own work, and until then — we can’t wait to see what you build! \ No newline at end of file diff --git a/sites/www/content/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/images/00gsPB_1VlYop9DPL.webp b/sites/www/content/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/images/00gsPB_1VlYop9DPL.webp new file mode 100644 index 0000000000..29218c78ef Binary files /dev/null and b/sites/www/content/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/images/00gsPB_1VlYop9DPL.webp differ diff --git a/sites/www/content/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/images/092qfA1AlxG0HMJCE.webp b/sites/www/content/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/images/092qfA1AlxG0HMJCE.webp new file mode 100644 index 0000000000..2fe0f1bf2f Binary files /dev/null and b/sites/www/content/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/images/092qfA1AlxG0HMJCE.webp differ diff --git a/sites/www/content/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/images/0FapWwJ2wFpi8l9ZZ.gif b/sites/www/content/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/images/0FapWwJ2wFpi8l9ZZ.gif new file mode 100644 index 0000000000..0e705f62b9 Binary files /dev/null and b/sites/www/content/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/images/0FapWwJ2wFpi8l9ZZ.gif differ diff --git a/sites/www/content/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/index.md b/sites/www/content/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/index.md new file mode 100644 index 0000000000..e729577e1b --- /dev/null +++ b/sites/www/content/blog/how-we-built-it-ask-dash-a-generative-ai-flutter-application/index.md @@ -0,0 +1,78 @@ +--- +title: "How we built it: Ask Dash — A generative AI Flutter application" +description: "As part of the Google Cloud Applied AI Summit, the Flutter and Vertex AI teams collaborated with Very Good Ventures to create an…" +publishDate: 2023-12-13 +author: verygoodopensource +image: images/092qfA1AlxG0HMJCE.webp +category: spotlight +layout: blog +--- + +## How we built it: Ask Dash — A generative AI Flutter application + +As part of the [Google Cloud Applied AI Summit](https://cloudonair.withgoogle.com/events/summit-applied-ml-summit-23?talk=t1_s5_vertexaisearchandconversation), the Flutter and Vertex AI teams collaborated with [Very Good Ventures](https://verygood.ventures/) to create an AI-powered Flutter demo app, [Ask Dash](https://github.com/VeryGoodOpenSource/dash_ai_search), using [Vertex AI Search and Conversation](https://cloud.google.com/vertex-ai-search-and-conversation) by Google Cloud. Vertex AI Search and Conversation empowers you to build and deploy search and conversational applications quickly with little to no experience in AI. Flutter was a great way to build a beautiful, customized search experience to show how both products can be used to build powerful applications in only a few short weeks! + +Vertex AI Search and Conversation allows you to create applications that interact with your data with personalized responses demonstrating the power of generative AI. Most importantly, it gives you full control over what data your application accesses and indexes so you can control what information is surfaced to which user. All application data and user interactions are stored in your own cloud instance and are never used to train Google’s underlying machine learning models. + +Since we used Flutter to build the demo app, we decided to use Flutter documentation as training data. We worked with the Google Cloud team to train the model specifically on the Flutter and Dart developer documentation to provide generative AI responses to questions like: What is Flutter? What platforms does it support? And what is hot reload? While much of this data is readily available in public AI models, this demo showcases how you can train a model on just your own data to create powerful AI experiences. + +This article takes you through how our partner, Very Good Ventures, built a Flutter web application and how we connected the app in the Cloud console. + +### How we built the Flutter web app + +The idea of creating a search application trained on the Flutter docs was straightforward. In fact, the [official Flutter documentation](https://docs.flutter.dev/) already provides a simple search experience that delivers relevant page results for questions on Flutter. However, when conceptualizing what to build, we wanted to demonstrate how Flutter can be used to create visually appealing interactive experiences that are fun and engaging. + +<DashImage figure src="images/092qfA1AlxG0HMJCE.webp" /> + + +**Creating interactive animations in Flutter** + +Flutter empowered the team to implement a wide array of animations seamlessly. Its rich set of animations, coupled with the flexibility of widgets, allowed us to create transitions, engaging motion effects, and fluid user interactions. From creating animated loading states while generating the results, to a Dash sprite that waves when the answers appear, Flutter provided flexibility to turn what could be a basic text response into something fun for users to interact with. + +<DashImage figure src="images/00gsPB_1VlYop9DPL.webp" /> + + +**Visualizing natural language search results** + +Unlike a traditional search experience, Vertex AI Search provides a natural language response to the question asked. The answer is generated with AI sourced from various pages within the Flutter documentation and presented as a summarized response alongside cards that display the relevant pages used by the AI. Each card provides the title to the page and a description so that the user can flip through the cards to get more context on the AI response. + +<DashImage figure src="images/0FapWwJ2wFpi8l9ZZ.gif" /> + + +Additionally, within the natural language response, Vertex AI Search provides a link to the source of each sentence in the response — giving the user a more granular understanding of where each part of the response originated. In the demo, clicking on the number next to each sentence flips the cards to the relevant source page. + +**More engaging in less time** + +Going into the project, we had a tight deadline to launch the demo at the [Google Cloud Applied AI Summit](https://cloudonair.withgoogle.com/events/summit-applied-ml-summit-23?talk=t1_s5_vertexaisearchandconversation). Flutter’s efficient development and ease-of-use, significantly expedited the development process for the team of two working on the application. It provided the necessary tools and framework to build complex animations efficiently, enabling our team to build and launch this demo in just under the two weeks leading up to the event. + +### Connecting the Flutter app to the Vertex API + +Integrating our front end web application with the Vertex AI Search API was achieved with a simple request using the [`http` package](https://pub.dev/packages/http). Without requiring any previous experience in building AI, Vertex AI Search provided answers to user-generated questions as JSON responses that were parsed and displayed within animated widgets. This allowed the team to focus on Flutter development to create an engaging experience out of the generated data. + +### Setting up generative AI search in your application + +Setting up Vertex AI and hosting the API for our Flutter app was also straightforward.. In our case, we used [https://docs.flutter.dev/](https://docs.flutter.dev/) as our data source and set it up directly in the Google Cloud console. As a Google Cloud customer, getting started with Vertex AI requires just three steps: + +1. **Create a data store** + +This is your website’s digital library, holding all the information you need to generate the AI model based on just the root URL. Google Cloud crawls your website for relevant data and creates a data source for you to query. To set this up in the Google Cloud Console,select **Search and Conversation**. Choose **Data Stores** and then **New Data Store**. Opt for **Website URL** as the source and provide your website’s URL. + +2. **Access your data** + +Next, create an app in Cloud Console to navigate the data indexed by the model and link it to the data store you created earlier. Under **Search and Conversation**, choose **Apps** and then **New App**. Select **Search** as the type and give your app a name that reflects its purpose, like Ask Dash. + +3. **Craft a Cloud Function** + +Finally, create a Cloud Function. This is the API wrapper that exposes your Vertex AI data to other applications. In the Console, go to **Cloud Functions** and select **Create Function**. + +That’s it! + +From there, use the API in your front-end application as you would any API to send requests and receive formatted responses that your application can display. To test it out, head to the **Function** page and select **Testing**. Enter a JSON object with a “search_term” key for your question (such as “hot reload”), and see a detailed response containing a natural language summary, relevant citations, and concise summaries of the referenced pages. + +Learn more about how to get started with [Vertex AI Search](https://cloud.google.com/generative-ai-app-builder/docs/try-enterprise-search) in Google Cloud’s documentation. + +### Generative AI applications built in Flutter + +To see Ask Dash in action and learn more about how we built it, check out the video session from the [Google Cloud Applied AI Summit](https://cloudonair.withgoogle.com/events/summit-applied-ml-summit-23?talk=t1_s5_vertexaisearchandconversation), where Alan Blount, a Product Manager for Google Cloud, breaks down the build process to show the potential of Vertex AI Search in a Flutter application. Check out the [open source Flutter code](https://github.com/VeryGoodOpenSource/dash_ai_search) for the demo and get started with your own AI search experience in Google Cloud Console. + +Ask Dash is just the start for how Flutter can power interactive Generative AI experiences in applications. We can’t wait to see what you build! \ No newline at end of file diff --git a/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/01aAyHb71XpChgH6R.webp b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/01aAyHb71XpChgH6R.webp new file mode 100644 index 0000000000..e1707b234e Binary files /dev/null and b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/01aAyHb71XpChgH6R.webp differ diff --git a/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/04ylubp836_tc4Nni.webp b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/04ylubp836_tc4Nni.webp new file mode 100644 index 0000000000..a4053ebd94 Binary files /dev/null and b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/04ylubp836_tc4Nni.webp differ diff --git a/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/0538Moipv7JTPricT.webp b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/0538Moipv7JTPricT.webp new file mode 100644 index 0000000000..530699e387 Binary files /dev/null and b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/0538Moipv7JTPricT.webp differ diff --git a/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/05i_oBZnXJonFcLc5.webp b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/05i_oBZnXJonFcLc5.webp new file mode 100644 index 0000000000..98387bd224 Binary files /dev/null and b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/05i_oBZnXJonFcLc5.webp differ diff --git a/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/0D0vBnIvT4TKXnRnG.webp b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/0D0vBnIvT4TKXnRnG.webp new file mode 100644 index 0000000000..c3bdd0e185 Binary files /dev/null and b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/0D0vBnIvT4TKXnRnG.webp differ diff --git a/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/0Nyoq1GrKd0eFce-M.webp b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/0Nyoq1GrKd0eFce-M.webp new file mode 100644 index 0000000000..407842d9ae Binary files /dev/null and b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/0Nyoq1GrKd0eFce-M.webp differ diff --git a/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/0Sf93caUyyPq4OKKa.webp b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/0Sf93caUyyPq4OKKa.webp new file mode 100644 index 0000000000..3be18d1796 Binary files /dev/null and b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/0Sf93caUyyPq4OKKa.webp differ diff --git a/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/0s1G59HNBAWqoq0xt.webp b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/0s1G59HNBAWqoq0xt.webp new file mode 100644 index 0000000000..d1f0ff0f63 Binary files /dev/null and b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/0s1G59HNBAWqoq0xt.webp differ diff --git a/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/0wpfH-WuFnd984LEW.webp b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/0wpfH-WuFnd984LEW.webp new file mode 100644 index 0000000000..d1fc2d6920 Binary files /dev/null and b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/images/0wpfH-WuFnd984LEW.webp differ diff --git a/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/index.md b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/index.md new file mode 100644 index 0000000000..6dbf595c04 --- /dev/null +++ b/sites/www/content/blog/how-we-built-it-creating-the-i-o-crossword-puzzle-powered-by-ai/index.md @@ -0,0 +1,90 @@ +--- +title: "How we built it: Creating the I/O Crossword puzzle, powered by AI" +description: "Adding a fun, helpful twist to a classic word game with Flutter, Firebase Genkit, and the Gemini API" +publishDate: 2024-06-26 +author: verygoodopensource +image: images/04ylubp836_tc4Nni.webp +category: spotlight +layout: blog +--- + +<DashImage figure src="images/04ylubp836_tc4Nni.webp" /> + + +For this year’s I/O, Very Good Ventures partnered with the Flutter and Firebase teams at Google to create a unique, digital experience to showcase the power of the Gemini API. + +The fun (and challenge) of crossword puzzles is solving the clues to complete the board. A natural place to start for words and clues was everything we’ve talked about at I/O this year. To generate the game content, we gave videos of the keynotes to [Gemini Advanced](https://gemini.google.com/) and asked it to create a list of topical technology-related words and clues to make playing the game a fun way to learn about everything announced this year at I/O. + +Keep reading to learn how we built the UI with Flutter and check out the open-source code for the game, which you can access on [GitHub](https://github.com/VGVentures/io_crossword). + +**How to play** + +When you log in to the puzzle, you are prompted to choose your team. As you correctly answer a clue, the word fills in and the cells change to your team color. Your team earns points for every word you solve and you earn a bonus for a streak of solving words without asking for hints. + +Need a hint? Click the **Hint** button, then ask up to ten yes-or-no questions about the concealed word. The [Gemini API](https://ai.google.dev/) privately responds to your questions so you can fill in more words and help your team claim Victory! + +<DashImage figure src="images/01aAyHb71XpChgH6R.webp" /> + + +The design of the *hint* feature is a great example of how different technologies can work together to solve a problem. Hints are powered by [Firebase Genkit](https://firebase.google.com/docs/genkit), a new framework for AI development announced at I/O, and are deployed as a Firebase Function. + +The network request to the API includes the answer to the clue, so to keep the response hidden from the client in the message data, we built a simple Dart backend using the [Dart Frog package](https://pub.dev/packages/dart_frog) to make the experience more robust. The Frog backend calls Genkit to get the hint and retrieves the answer from the database. That way players can’t simply inspect the network call to figure out the answers to the game. + +You can read more about how the Genkit flow was built in the [Firebase deep dive blog](https://firebase.blog/posts/2024/06/ai-powered-crossword-genkit). + +<DashImage figure src="images/05i_oBZnXJonFcLc5.webp" /> + + +**Rendering a performant crossword board with Flutter** + +The board is one of the core pieces of the game. We optimized player movement on the puzzle to ensure good performance and make the best user experience for players. + +We considered two options to build the board: straight Flutter or leverage the [Flame](https://flame-engine.org/) game engine. The most compelling feature of Flame for this game is its camera API, which allows the mascots to easily move and supports zoom controls. However, since that was really the only feature of Flame we would be using, we ultimately decided that using a full game engine like Flame would be overkill for this scenario. + +Looking for an alternative solution, we explored the [`InteractiveViewer`](https://api.flutter.dev/flutter/widgets/InteractiveViewer-class.html) widget, which allows rendering the words in a canvas of a custom size, and moving the mascots around freely with matrix transformations. `InteractiveViewer` was a simpler solution for our needs with less dependencies and load, which better allowed us to showcase the flexibility and performance of Flutter. + +**The power and flexibility of the InteractiveViewer** + +The `InteractiveViewer` has built-in zoom gestures, but we wanted to add more intuitive buttons for the desktop experience. Leveraging the matrix transformations, we implemented zoom controls by first calculating the change in scale and updating the new viewport with the center as a constant reference point: + +<DashImage figure src="images/0D0vBnIvT4TKXnRnG.webp" /> + + +Once we have the new tentative viewport, we have to ensure that it fits within the boundaries of the board. We have two scenarios that we must allow for: when the viewport is bigger than the board, or when it’s positioned out of bounds. We update both the change in scale and the translation of the viewport to ensure it fits within the boundaries by tweaking the zoom level or the location of the viewport, as shown in the following code: + +<DashImage figure src="images/0wpfH-WuFnd984LEW.webp" /> + + +Finally, calculate the transformation and apply it to the `InteractiveViewer` controller: + +<DashImage figure src="images/0Sf93caUyyPq4OKKa.webp" /> + + +With this code, we’ve extended the zoom controls of the `InteractiveViewer` and transformed the viewport according to our needs. + +**Boosting performance with WebAssembly in Flutter** + +WebAssembly support for Flutter web apps was one of the major announcements for the Flutter community this year at IO. With players across the world playing the game at the same time, performance was a critical factor. We leveraged [WebAssembly (Wasm)](https://docs.flutter.dev/platform-integration/web/wasm), built into Flutter, to reduce performance bottlenecks and maintain a smooth frame rate, particularly when it comes to the character and board animations in the game. + +<DashImage figure src="images/0Nyoq1GrKd0eFce-M.webp" /> + + +**Dart Backend and Cloud Run: Seamless code sharing between backend and frontend** + +To ensure a smooth gaming experience for all users, our Dart backend is built with the [Dart Frog package](https://pub.dev/packages/dart_frog) and is hosted on [Google Cloud Run,](https://dartfrog.vgv.dev/docs/deploy/google-cloud-run) to leverage the auto scaling capabilities. This helps to maintain optimal performance regardless of the active number of players as the game makes multiple calls, such as every time a user is created, or when a player submits a word or asks for a hint. + +Using a backend to confirm the answers to each clue also allowed us to safeguard the crossword puzzle and prevent cheating. Specifically, the app reads information with the Flutter Firestore SDK but the database *only* allows changes from the Dart back end. This also allowed for faster development since we could use the same language (Dart) between the front end and the back end. + +We can see this pattern in action in the Player data model, for example, which creates a player using the Dart Frog API. + +<DashImage figure src="images/0s1G59HNBAWqoq0xt.webp" /> + + +The app directly accesses the players leaderboard, reusing the same model and avoiding duplication and desynchronization: + +<DashImage figure src="images/0538Moipv7JTPricT.webp" /> + + +**Start playing: Solve the I/O Crossword puzzle!** + +Start playing the [crossword](https://crossword.withgoogle.com/) on your own in all its across-and-down glory. For those who want to dig into the details, check out its [open-source code](https://github.com/VGVentures/io_crossword) and the [Developer Learning Pathway](https://developers.google.com/learn/pathways/solution-crossword) to showcase how we built it. Catch the [recap of Google I/O](https://www.youtube.com/watch?v=xKmEOXZsU_0) to learn more about everything announced this year! \ No newline at end of file diff --git a/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/0-yT0Ipqt2zlSl--a.webp b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/0-yT0Ipqt2zlSl--a.webp new file mode 100644 index 0000000000..b4c2c56b34 Binary files /dev/null and b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/0-yT0Ipqt2zlSl--a.webp differ diff --git a/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/06d7cgqMRk3cxCH0V.webp b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/06d7cgqMRk3cxCH0V.webp new file mode 100644 index 0000000000..5bd463ce65 Binary files /dev/null and b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/06d7cgqMRk3cxCH0V.webp differ diff --git a/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/08bFlQeCsOjn4dJOM.webp b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/08bFlQeCsOjn4dJOM.webp new file mode 100644 index 0000000000..e4a13dfbc0 Binary files /dev/null and b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/08bFlQeCsOjn4dJOM.webp differ diff --git a/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/0Hdrg2nvtSyrnyC9v.webp b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/0Hdrg2nvtSyrnyC9v.webp new file mode 100644 index 0000000000..190c648f51 Binary files /dev/null and b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/0Hdrg2nvtSyrnyC9v.webp differ diff --git a/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/0RhjtQusRV-2jnKR1.webp b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/0RhjtQusRV-2jnKR1.webp new file mode 100644 index 0000000000..00c630c205 Binary files /dev/null and b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/0RhjtQusRV-2jnKR1.webp differ diff --git a/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/0c-MqZJ30E1AOVgrw.webp b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/0c-MqZJ30E1AOVgrw.webp new file mode 100644 index 0000000000..179872e57d Binary files /dev/null and b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/0c-MqZJ30E1AOVgrw.webp differ diff --git a/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/0sqHRiY_r9CnBz774.webp b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/0sqHRiY_r9CnBz774.webp new file mode 100644 index 0000000000..4054b747c7 Binary files /dev/null and b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/0sqHRiY_r9CnBz774.webp differ diff --git a/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/1cAJxrRQt1MrmnDJ2CkZrNQ.webp b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/1cAJxrRQt1MrmnDJ2CkZrNQ.webp new file mode 100644 index 0000000000..e3ccaea8cf Binary files /dev/null and b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/1cAJxrRQt1MrmnDJ2CkZrNQ.webp differ diff --git a/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/1gKUzs2GxDWCBIS-i218_zQ.webp b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/1gKUzs2GxDWCBIS-i218_zQ.webp new file mode 100644 index 0000000000..c7dd6a1737 Binary files /dev/null and b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/1gKUzs2GxDWCBIS-i218_zQ.webp differ diff --git a/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/1gm1C-7frVIwrLDmSJIHOhQ.webp b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/1gm1C-7frVIwrLDmSJIHOhQ.webp new file mode 100644 index 0000000000..a5892007b4 Binary files /dev/null and b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/images/1gm1C-7frVIwrLDmSJIHOhQ.webp differ diff --git a/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/index.md b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/index.md new file mode 100644 index 0000000000..8279bcea1f --- /dev/null +++ b/sites/www/content/blog/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition/index.md @@ -0,0 +1,134 @@ +--- +title: "How we use user surveys to prioritize engineering effort in Flutter — October 2018 edition" +description: "Written by Ja Young Lee & Tao Dong" +publishDate: 2019-01-17 +author: jayoung-lee +image: images/1gKUzs2GxDWCBIS-i218_zQ.webp +category: announcements +layout: blog +--- + +## How we use user surveys to prioritize engineering effort in Flutter — October 2018 edition + +Flutter UX research team is back to share the results of our latest user survey! This online survey was conducted from Oct 29th to Nov 12nd, *before Flutter 1.0 was announced*. + +We received responses from 1,966 users, distributed in 108 different countries and regions. The number of respondents has almost doubled since the [last survey in July](https://medium.com/flutter-io/what-weve-learned-from-the-july-2018-flutter-user-survey-cbbf1e04370c), from 1,016. Thank you all for taking your time to answer the questions! + +[**What We’ve Learned from the July 2018 Flutter User Survey** +*Flutter’s User Experience Research team recently conducted an online survey to measure user satisfaction and understand…*medium.com](https://medium.com/flutter-io/what-weve-learned-from-the-july-2018-flutter-user-survey-cbbf1e04370c) + +## Are users satisfied with Flutter? + +Our users were generally satisfied with Flutter. 93% of the respondents said that they are very satisfied or somewhat satisfied with Flutter, one percent point higher than the results from July 2018. Given that our user base has grown tremendously in 2018, we believe Flutter is doing well at keeping up with the expectations of incoming new users. + +In addition, users still found Flutter very helpful or extremely helpful in reaching their full potential engineering velocity and implementing ideal UI (80% and 79% respectively), which are almost identical to the last survey’s results (79% and 80% respectively). + +When it comes to performance of Flutter, 86% were very satisfied or somewhat satisfied. We asked this question to those who have used Flutter in “release” mode, which is mode optimized for fast startup, fast execution, and small package sizes with all debugging aids disabled. If you haven’t already, you can check out your app’s actual performance in [release mode](https://flutter.io/docs/testing/build-modes). + +One other interesting finding was that a larger percentage of advanced Flutter users (self-reported) were more satisfied with the Dart programming language, as you can see in the graph below. This result may be interpreted in a couple ways. One possibility is that users appreciate Dart more as they gain more experience in the context of developing Flutter apps. The other possibility is that users new to Flutter are skeptical about learning a new language or need a better introduction to Dart to overcome the initial fear of learning a new language. As the user gains more experience, they discover that Dart is similar to many other object-oriented programming languages. Either way, it tells us that we need to support new users better so that they can quickly and easily transition to Dart, and understand the unique benefits of Dart for Flutter development. + +<DashImage figure src="images/1cAJxrRQt1MrmnDJ2CkZrNQ.webp" alt="Satisfaction with Dart by self-reported level of experience with Flutter" caption="Satisfaction with Dart by self-reported level of experience with Flutter" /> + + +We also found that the proportion of respondents using Flutter for their main job has increased from 23% to 30%, while the proportion evaluating Flutter for potential production has decreased from 45% to 40%. We’re glad that more developers seemed to have moved from evaluating Flutter to using Flutter in production. + +<DashImage figure src="images/1gKUzs2GxDWCBIS-i218_zQ.webp" /> + + +In terms of the packages and plugin ecosystem, the percentage of users who are somewhat or very satisfied has decreased to 74% from 83%. It was a somewhat surprising result, because there has been rapid growth in the number of packages available to Flutter developers. At the end of July, there were 420 packages, and there are now [over 1,200 packages](https://pub.dartlang.org/flutter/packages?q=dependency%3Aflutter). We attributed this decrease in satisfaction to growing expectations of the maturity of Flutter’s package ecosystem, as Flutter itself approached 1.0. We expect this measure to improve as we deliver and refine more first-party plugins such as WebView and Google Maps (added after this survey) and more community members contribute to Flutter’s ecosystem. + +We also found that more users are working on Flutter apps that have already been released, compared to the survey in July (15% → 25%), and more users are working on a Flutter app that is about to be released within the next 6 months (52% → 60%). Even before Flutter 1.0 was released, over 3,000+ Flutter apps were available on the Play Store, and based on these survey results, we are looking forward to seeing many more Flutter apps in the near future. To check out some of the apps, refer to the collection of Flutter apps archived by the community, “[It’s all widgets](https://itsallwidgets.com/)”. + +## How did the feedback from the last time turn into improvements? + +Broad-scale surveys like this are an important way for us to capture user feedback, and we adjust our plans regularly based on this feedback. Hence, [the survey we ran in July](https://medium.com/flutter-io/what-weve-learned-from-the-july-2018-flutter-user-survey-cbbf1e04370c) had an impact on our journey to improve Flutter. We’d like to share some of our efforts to respond to your feedback. + +First, users wanted Flutter to better support building iOS style UIs from the July survey. So we worked on many improvements to Cupertino widgets. Below are some changes that were made since the last survey. + +<DashImage figure src="images/06d7cgqMRk3cxCH0V.webp" alt="[CupertinoActionSheet](https://docs.flutter.io/flutter/cupertino/CupertinoActionSheet-class.html)" caption="[CupertinoActionSheet](https://docs.flutter.io/flutter/cupertino/CupertinoActionSheet-class.html)" /> + + +<DashImage figure src="images/08bFlQeCsOjn4dJOM.webp" alt="[CupertinoDatePicker](https://docs.flutter.io/flutter/cupertino/CupertinoDatePicker-class.html)" caption="[CupertinoDatePicker](https://docs.flutter.io/flutter/cupertino/CupertinoDatePicker-class.html)" /> + + +<DashImage figure src="images/0RhjtQusRV-2jnKR1.webp" alt="[CupertinoSegmentedControl](https://docs.flutter.io/flutter/cupertino/CupertinoSegmentedControl-class.html)" caption="[CupertinoSegmentedControl](https://docs.flutter.io/flutter/cupertino/CupertinoSegmentedControl-class.html)" /> + + +<DashImage figure src="images/0Hdrg2nvtSyrnyC9v.webp" alt="Hero Nav" caption="Hero Nav" /> + + +<DashImage figure src="images/0sqHRiY_r9CnBz774.webp" alt="[CupertinoAlertDialog](https://docs.flutter.io/flutter/cupertino/CupertinoAlertDialog-class.html)" caption="[CupertinoAlertDialog](https://docs.flutter.io/flutter/cupertino/CupertinoAlertDialog-class.html)" /> + + +For more information, please see the [change log](https://github.com/flutter/flutter/wiki/Changelog). We found that this led to **14%** increase in the proportion of users very satisfied or somewhat satisfied with Cupertino widgets (from 58% to 72%). + +We also received lots of comments on the challenges in choosing and implementing state management from the last survey. In the redesigned Flutter.io website launched in November, we added [a new page](https://flutter.io/docs/development/data-and-backend/state-mgmt) to list useful resources on state management. We are working on a more substantial guide on state management. Please check the [flutter.io](https://flutter.io) website for further updates. To track improvements in this area, we started to measure ease of choosing state management patterns and will continue to monitor the metric in future surveys. + +The effort in improving documentation was not limited to state management. From the last survey we learned that the hardest thing to do with the current documentation was to“find sample code”. Responding to the gap, our team has been adding more samples to the documentation. We hope you enjoy the new design of sample code added to the API docs as well. You can easily copy the code snippet using the “copy all” button, or visit the “Sample in an app” tab and create a full sample app from a command line, using the unique ID of the app. + +<DashImage figure src="images/1gm1C-7frVIwrLDmSJIHOhQ.webp" alt="Sample code of a Card class. The clipboard icon on the upper right corner help you copy the entire code at once." caption="Sample code of a Card class. The clipboard icon on the upper right corner help you copy the entire code at once." /> + + +<DashImage figure src="images/0c-MqZJ30E1AOVgrw.webp" alt="Sample app for a Card class. You can use the clipboard icon to copy the code for this app or use the command to create an app from the command line. See the gif below!" caption="Sample app for a Card class. You can use the clipboard icon to copy the code for this app or use the command to create an app from the command line. See the gif below!" /> + + +<DashImage figure src="images/0-yT0Ipqt2zlSl--a.webp" alt="Create a sample app from the command line" caption="Create a sample app from the command line" /> + + +## How are we prioritizing our engineering effort with users’ feedback this time? + +If you participated in the survey, you might remember that in the last section of the survey, we asked you to choose the most and the least valuable feature from a list of six features and repeat that. The section was designed particularly to determine relative priorities of the potential features and to maximize the difference based on a well-established survey method called [Maxdiff](https://www.surveyanalytics.com/max-diff/). + +Using the Maxdiff method, the survey forced respondents to make extreme decisions so that we can give different weights to the features being compared against one another. There were 21 features in the survey, which were sourced mostly from GitHub issues with high numbers of reactions. The features covered various topics like IDE tools, new plugins, Dart, etc. The results helped us prioritize our limited engineering resources. Please continue to vote on the [issues](http://github.com/flutter/flutter/issues) you care about, so that we can include emerging needs in future surveys. + +In this article, we’d like to share the features that were relatively highly ranked in the survey, and briefly describe what we are going to do to respond to the high needs. Before you read, however, please note that we have other features that are not included in the survey, because they are already on our roadmap, for example, C/C++ integration (GitHub issue [#7053](https://github.com/flutter/flutter/issues/7053)), and plugin for Crashlytics (GitHub issue [#14765](https://github.com/flutter/flutter/issues/14765)). + +Also, we would like to emphasize that while we take the survey results seriously, features may not be scheduled for implementation in the exact order in the survey results, because different features may require substantially different amounts of resources and have different challenges. We also prioritize features that may impact a greater number of end-users or where we have additional signals that suggest a feature is of high value. + +For the features listed below, we do not have confirmed plans yet for the first half of 2019. However, these features are on our radar, and we would like to continue to actively investigate what is needed to meet the needs. + +* [Ranked #1] Built-in support for queryable data storage + +* [Ranked #2] Support for building Windows and macOS applications + +* [Ranked #3] Support for dynamic platform theming mechanism (changing between Material and Cupertino, depending on runtime platform) + +* [Ranked #4] Better support for saving and restoring state + +* [Ranked #6] Built-in support for local notifications + +* [Ranked #9] Ability for a Flutter app to register a URL handler for deep linking + +* [Ranked #10] Built-in support for monitoring network connectivity status + +* [Ranked #13] A better build system (e.g., support for codegen, integration with gradle, etc.) + +* [Ranked #15] Testing framework for Flutter apps with platform-specific code (e.g., plugins) + +* [Ranked #17] Network and cache insights tool + +“No confirmed plan” does not mean that we have no plan at all. For example, we are currently researching what options are available for queryable data storage. Also, to support desktop applications, we are investing in supporting core desktop-class UI metaphors, such as mouse scrolling, hover effects, non-primary mouse buttons, and clipboard, as well as support for Android on ChromeOS and Hummingbird (Flutter on Web on desktop). There will be more work to be done beyond this year before we consider Flutter a serious option for writing desktop software. + +Good news is that we have features with settled plans. + +* [Ranked #5] Support for using Flutter to develop mobile web apps + +* [Ranked #7] Code push / hot update for pushing out-of-store updates to apps + +* [Ranked #8] Built-in support for in-app payments, including subscriptions + +* [Ranked #14] Better performance (frame rendering) diagnosis tool (initial version is already released) + +* [Ranked #16] Using control flow elements within a build method (e.g., to conditionally omit one or more elements) + +* [Ranked #19] Better memory usage diagnosis tool + +You will also be able to find 2019 roadmap on Flutter’s GitHub [Wiki](https://github.com/flutter/flutter/wiki/Roadmap) soon. + +Flutter is an open-source project. The list above does not define what Flutter will contain. Instead, it is a list of what *we* will work on, because we consider these features important for the community and the end-users. We always welcome high-quality contributions from other parties, who have a different set of needs. + +## What’s next? + +We plan to run another user survey again and collect your feedback on various aspects of Flutter, especially after the launch of [Flutter 1.0](https://developers.googleblog.com/2018/12/flutter-10-googles-portable-ui-toolkit.html). Please look out for the next survey, which will be announced on the [flutter.io website](https://flutter.io) and community channels like Twitter ([@flutterio](https://www.twitter.com/flutterio)) and Reddit ([r/FlutterDev](https://www.reddit.com/r/FlutterDev/)). + +Flutter’s UX research team is also doing a wide variety of user experience studies to make your experience with Flutter more pleasant. If you are interested in participating in our studies, you can [sign up](https://docs.google.com/forms/d/e/1FAIpQLSe0i4De809KXVCdljGKrjMj3lxhuzbuFKCtY5PEQPCYtGxFMg/viewform) for the future user experience studies. \ No newline at end of file diff --git a/sites/www/content/blog/hummingbird-building-flutter-for-the-web/images/0gD64Y8ECWBBuSZrx.webp b/sites/www/content/blog/hummingbird-building-flutter-for-the-web/images/0gD64Y8ECWBBuSZrx.webp new file mode 100644 index 0000000000..fdedc3dfe8 Binary files /dev/null and b/sites/www/content/blog/hummingbird-building-flutter-for-the-web/images/0gD64Y8ECWBBuSZrx.webp differ diff --git a/sites/www/content/blog/hummingbird-building-flutter-for-the-web/images/1KpntaDMPfVygd3iTCUgI1A.webp b/sites/www/content/blog/hummingbird-building-flutter-for-the-web/images/1KpntaDMPfVygd3iTCUgI1A.webp new file mode 100644 index 0000000000..9bef4fb2f7 Binary files /dev/null and b/sites/www/content/blog/hummingbird-building-flutter-for-the-web/images/1KpntaDMPfVygd3iTCUgI1A.webp differ diff --git a/sites/www/content/blog/hummingbird-building-flutter-for-the-web/images/1M0ik7rqmkK1Cf0xB4iwbxg.webp b/sites/www/content/blog/hummingbird-building-flutter-for-the-web/images/1M0ik7rqmkK1Cf0xB4iwbxg.webp new file mode 100644 index 0000000000..092e8fc982 Binary files /dev/null and b/sites/www/content/blog/hummingbird-building-flutter-for-the-web/images/1M0ik7rqmkK1Cf0xB4iwbxg.webp differ diff --git a/sites/www/content/blog/hummingbird-building-flutter-for-the-web/images/1pUNwEsIf3HKaarmNVRwP7Q.webp b/sites/www/content/blog/hummingbird-building-flutter-for-the-web/images/1pUNwEsIf3HKaarmNVRwP7Q.webp new file mode 100644 index 0000000000..9d2a985150 Binary files /dev/null and b/sites/www/content/blog/hummingbird-building-flutter-for-the-web/images/1pUNwEsIf3HKaarmNVRwP7Q.webp differ diff --git a/sites/www/content/blog/hummingbird-building-flutter-for-the-web/images/1srIZflrlT4IhxER1nnOtJw.webp b/sites/www/content/blog/hummingbird-building-flutter-for-the-web/images/1srIZflrlT4IhxER1nnOtJw.webp new file mode 100644 index 0000000000..62b78dfb01 Binary files /dev/null and b/sites/www/content/blog/hummingbird-building-flutter-for-the-web/images/1srIZflrlT4IhxER1nnOtJw.webp differ diff --git a/sites/www/content/blog/hummingbird-building-flutter-for-the-web/images/1yjnmm_UQ6wrQD5AssXxqLg.webp b/sites/www/content/blog/hummingbird-building-flutter-for-the-web/images/1yjnmm_UQ6wrQD5AssXxqLg.webp new file mode 100644 index 0000000000..f955ac6ec6 Binary files /dev/null and b/sites/www/content/blog/hummingbird-building-flutter-for-the-web/images/1yjnmm_UQ6wrQD5AssXxqLg.webp differ diff --git a/sites/www/content/blog/hummingbird-building-flutter-for-the-web/index.md b/sites/www/content/blog/hummingbird-building-flutter-for-the-web/index.md new file mode 100644 index 0000000000..949293db9a --- /dev/null +++ b/sites/www/content/blog/hummingbird-building-flutter-for-the-web/index.md @@ -0,0 +1,176 @@ +--- +title: "Hummingbird: Building Flutter for the Web" +description: "At Flutter Live today, we announced that we are experimenting with running Flutter on the Web. In this post, we describe how we are…" +publishDate: 2018-12-04 +author: yegorj +image: images/0gD64Y8ECWBBuSZrx.webp +category: spotlight +layout: blog +--- + +At Flutter Live today, we announced that we are experimenting with running Flutter on the Web. In this post, we describe how we are approaching the challenge, and the current state of the technology. At the end of the post you will find answers to questions about interop and embedding. + +<DashImage figure src="images/0gD64Y8ECWBBuSZrx.webp" /> + + +Let’s begin with a quick refresher of Flutter’s architecture. Flutter is a multi-layered system, such that higher layers are easier to use and allow you to express a lot with little code, and the lower layers give you more control at the expense of having to deal with some complexity. When a higher layer does not do what a developer wants, they can drop to a lower layer. Developers have access to all the layers above the Flutter Engine. + +<DashImage figure src="images/1pUNwEsIf3HKaarmNVRwP7Q.webp" alt="Flutter for Mobile Architecture" caption="Flutter for Mobile Architecture" /> + + +Flutter Engine is exposed as the lowest-level library in Flutter, *dart:ui*. It knows nothing about widgets, physics, animation, or layout (except text layout). All it knows is how to compose [pictures](https://docs.flutter.io/flutter/dart-ui/Picture-class.html) onto the screen and turn them into pixels. It would be hard to write applications directly on top of dart:ui. This is why the higher layers were created. + +Everything above dart:ui is what we like to call “the framework”. Everything below it is “the engine”. The framework is written entirely using the Dart programming language. Most of the engine is written in C++, with Android-specific parts written in Java, and iOS-specific parts written in Objective-C. Some basic classes and functions in dart:ui are written in Dart and mostly serve as bridges between Dart and C++. + +Flutter also offers a plugin system. Plugins are code written in a language that has direct access to the OEM libraries and third-party libraries that the mobile ecosystems have accumulated over time. To create a plugin for Android you would write Java or Kotlin. An iOS plugin is written using Objective-C or Swift. + +## Hello, The Web + +The Web platform has been evolving for decades and includes numerous technologies and specifications. There are a few umbrella terms used to describe large collections of related features: HTML, CSS, SVG, JavaScript, WebGL. In order to run Flutter on the Web we need to: + +* **Compile Dart code:** Flutter is written in Dart and we need to run Dart on the Web. + +* **Choose a subset of Flutter to run on the Web**: it is not practical or useful to run all Flutter code on the Web. Some of it is platform specific, such as the Android and iOS bits. + +* **Choose a sufficient subset of Web features**: over time the Web Platform accumulated features that overlap in functionality. For example, you can draw graphics using HTML+CSS, SVG, Canvas, and WebGL. + +Dart has been compiling to JavaScript for as long as the language has existed. Many important applications compile from Dart to JavaScript and run in production today. Flutter’s compilation strategy relies on this same infrastructure. + +When we began our exploration we were faced with several choices for UI rendering. We quickly realized that the specific Flutter layers we want to support dictate the Web technologies we would use for implementation. We built three **prototypes**: + +1. **Just widgets**: This prototype implemented Flutter’s widget framework and offered a set of core layout widgets as a foundation for building custom widgets. For layout and positioning it relied on the Web’s built-in capabilities, such as flexbox, grid layout, browser scrolling via `overflow:scroll`, etc. + +1. **Widgets + custom layout**: This prototype included Flutter’s layout system (provided by `RenderObject`), but mapped render objects directly to HTML elements. + +1. **Flutter Web Engine**: This prototype retained all layers above dart:ui and provided a dart:ui implementation that runs in the browser. + +One of the most valuable features of Flutter is that it is *portable* across platforms. While you can (and sometimes are encouraged to) write custom platform-specific code, the code that does not need to be different across platforms can be shared. This allows writing applications targeting multiple platforms with a single codebase. + +After attempting to port several sample applications to the Web we realized that prototypes #1 and #2 would not provide the level of portability that Flutter developers have come to enjoy. We therefore decided to go with prototype #3, the Flutter Web Engine design, since this would allow the highest framework-level code reuse between platforms: + +<DashImage figure src="images/1M0ik7rqmkK1Cf0xB4iwbxg.webp" alt="*Flutter for the Web Architecture (Hummingbird)*" caption="*Flutter for the Web Architecture (Hummingbird)*" /> + + +Now that we know we want to implement the entire dart:ui API we need to pick a set of Web technologies to build on top of. Flutter renders UI one frame at a time. Within each frame Flutter ***builds*** widgets, performs ***layout***, and finally ***paints*** them on the screen. + +## Building widgets + +The widget building mechanism does not depend on the environment the app is running in. The process simply instantiates in-memory objects, tracks their state, and when the state changes computes the minimal updates necessary for the lower levels of the system, layout and painting. Porting this part to the Web was straight-forward. After the Dart team implemented super-mixin support in dart2js, the compiler compiled all of the widgets and the widget framework to JavaScript with almost no issues. + +## Layout + +The layout system is a little trickier. The biggest challenge was text layout. Everything else — Center, Row, Column, Stack, Scrollable, Padding, Wrap, and so on — is laid out by the framework and therefore compiles to the Web with no modifications. + +In Flutter you lay out a paragraph of text by creating a [Paragraph](https://docs.flutter.io/flutter/dart-ui/Paragraph-class.html) object and calling its [layout()](https://docs.flutter.io/flutter/dart-ui/Paragraph/layout.html) method. Unfortunately, the Web lacks a direct text layout API. The trick we used to measure text’s layout properties was to cause the browser to lay it out, then read back the relevant properties from the DOM elements. + +When laying out a paragraph of text Flutter measures the paragraph’s height, width, maximum intrinsic width, minimum intrinsic width, and alphabetic and ideographic baselines. These properties are illustrated below. + +<DashImage figure src="images/1KpntaDMPfVygd3iTCUgI1A.webp" alt="*Paragraph layout attributes*" caption="*Paragraph layout attributes*" /> + + +You can find more details in Flutter’s [Paragraph documentation](https://docs.flutter.io/flutter/dart-ui/Paragraph-class.html). + +To measure these properties we first put a paragraph in an HTML DOM element, then we read the dimensions of the element. This causes the browser to lay it out. For example, to get the element’s width and height we call [offsetWidth](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetWidth) and its sibling, [offsetHeight](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetHeight). To measure the baseline we place the paragraph in an element configured to lay itself out using flex row. Next to the paragraph we place another element called “probe”. Because the probe is aligned with the text’s baseline calling [getBoundingClientRect](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect) on it gives us the baseline. We use similar tricks to measure minimum and maximum intrinsic widths. + +## Painting + +Last but not least, we need to paint the widgets. This area has seen the most churn during our exploration, and it still is one of the most active areas of research for us. By the end of the frame all our widgets need to be turned into pixels on the screen. In the browser, this means they have to be boiled down to some combination of HTML/CSS, Canvas, SVG, and WebGL. + +We have not looked at WebGL yet, mostly because it is low level and requires that we reimplement things that browsers can already do, such as text layout and rasterizing 2D graphics, but also because we have not yet figured out how accessibility, text selection, and composition with non-Flutter components can work with WebGL. + +One of our early prototypes produced an HTML element for every RenderObject. We did get promising results, however that turned out to be too much of a breaking API change. We would have to maintain a massive code delta with Flutter, so we shelved the idea. + +We are currently simultaneously exploring two approaches: + +* HTML+CSS+Canvas + +* CSS Paint API + +### HTML+CSS+Canvas + +With this approach we categorize pictures produced by the framework into those expressible using HTML+CSS, and those expressible using Canvas 2D. We then output HTML DOM that combines HTML, CSS, and 2D canvases. + +We prefer HTML+CSS because it is backed by the browser’s [display list](https://en.wikipedia.org/wiki/Display_list). This means we can leave optimizing the rasterization of pictures to the browser’s rendering engine. This also means we can apply arbitrary transforms, particularly rotations and scaling, without worrying about pixelation. We call this canvas implementation *DomCanvas*. + +If we are unable to express a picture using HTML+CSS, we fall back to canvas. Canvas 2D allows us to paint nearly all of Flutter drawing commands. If you compare Flutter’s [Canvas](https://docs.flutter.io/flutter/dart-ui/Canvas-class.html) to the Web’s [CanvasRenderingContext2D](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D) you find many similarities. Painting on the canvas is efficient because it does not create a mutable tree of nodes that needs to be maintained over time, like HTML DOM or SVG do. + +One challenge with the 2D canvas is that browsers represent it as a bitmap, a buffer of memory that stores *Width* x *Height* pixels. As a result scaling a canvas results in pixelation. If scaling results in resizing the picture we need to resize the canvas. We found that allocating canvases is fairly expensive, and so is resizing them. On top of that, when compositing multiple canvases onto the same page the browser has to perform raster composition, which also shows up in our profiles. Compositing rasters works differently from display lists. You can paint multiple display lists onto the same memory buffer. We call Canvas 2D-backed canvas implementation *BitmapCanvas*. We are researching ways to make bitmap canvases more efficient. + +To express Flutter’s opacity, transform, offset, clip rect, and other [layers](https://docs.flutter.io/flutter/rendering/Layer-class.html) we use plain HTML elements. For example, an opacity layer becomes an `<flt-opacity>` element with the `opacity` CSS attribute on it, a transform layer becomes an `<flt-transform>` element with the `transform` CSS attribute, and clip rect becomes an `<flt-clip-rect>` with `overflow: hidden`. + +When all is said and done, a frame is rendered onto the page as a tree of HTML elements with DomCanvas and BitmapCanvas as leaf nodes. For example: + +<DashImage figure src="images/1srIZflrlT4IhxER1nnOtJw.webp" alt="*Sample HTML DOM structure of a frame*" caption="*Sample HTML DOM structure of a frame*" /> + + +An equivalent Flutter layer tree (called [*flow layer*](https://github.com/flutter/engine/tree/master/flow/layers)) in the Flutter Engine would look like this: + +<DashImage figure src="images/1yjnmm_UQ6wrQD5AssXxqLg.webp" alt="*Sample Flutter Engine layer structure*" caption="*Sample Flutter Engine layer structure*" /> + + +Structurally they are very similar. The biggest difference is that on the Web we have to pick different implementations for pictures based on their contents. + +HTML+CSS+Canvas works in all modern browsers. However, we are already looking into the future: + +### CSS Paint API + +[CSS Paint](https://developers.google.com/web/updates/2018/01/paintapi) is a new Web API and part of a bigger effort, [Houdini](https://developers.google.com/web/updates/2016/05/houdini). Houdini is a collaboration between many browser vendors to expose certain parts of the CSS engine to developers. In particular, the CSS Paint API allows developers paint custom graphics into HTML elements when those elements request painting. For example, you may assign the painting of an element’s `background` to a custom CSS painter. It is very similar to canvas but with the following important differences: + +* The painting is not done by the main JavaScript isolate, but by something called a *paint worklet*. It is a bit like a web worker in that it has its own memory space. A paint worklet executes during the browser’s paint phase, after the DOM changes are committed. + +* CSS paint is backed by a display list, not a bitmap. This gives us the best of both worlds — the 2D canvas-like painting efficiency *and* no pixelation. + +* Currently CSS paint does not support painting text. + +As of this writing, Chrome and Opera are the only browsers to support CSS Paint in production. However, other browsers [are in various stages of shipping](https://ishoudinireadyyet.com/) their implementations. + +We have experimental support for the CSS Paint API in Flutter for Web and it shows good results already, particularly in performance. Our implementation simply serializes paint commands into a custom CSS property. The paint worklet reads those commands and executes them. We render text using the normal `<p>` and `<span>` HTML elements. + +Our current serialization mechanism isn’t particularly efficient — it’s a tree of nested lists converted to JSON — but part of the Houdini project is to add support for [typed array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays). When it becomes available we will encode paint commands as typed arrays instead of JSON strings. Typed arrays are [transferable](https://developer.mozilla.org/en-US/docs/Web/API/Transferable), which means they can be passed from the main isolate to the paint worklet by reference. No copying of memory involved. + +## Interop and embedding + +### Calling Dart libraries from Flutter + +Flutter web apps have full access to all existing Dart libraries that run on the Web today. + +### Calling JavaScript libraries from Flutter + +Flutter web apps fully support Dart’s JS-interop packages: `package:js` and `dart:js`. + +### Using CSS in Flutter web apps + +Currently Flutter assumes full control over the web page for correctness and performance. For example, we only use a small subset of CSS that follows certain performance guidelines, such as [https://csstriggers.com/](https://csstriggers.com/). Putting arbitrary CSS on the page may cause Flutter behave unpredictably. + +Another reason to avoid CSS in a Flutter for Web app is because, by design, Flutter needs to know all the layout properties as it renders the frame. CSS acts as a blackbox. For example, if you want to display a scrollable list of widgets you have to instantiate and generate HTML for all of them and apply the necessary CSS properties (e.g. flex-direction row and overflow: scroll). Then the browser lays everything out and renders it to screen. Application code does not participate in the layout process. + +Finally, in the spirit of keeping Flutter code portable across platforms, we avoid CSS so we can run the same code natively on Android and iOS. + +### Embedding Flutter in existing web-apps + +We have not yet added proper support for this, but we intend to explore it in the future. A couple of approaches we are considering are `<iframe>` and shadow DOM. + +### Embedding non-Flutter components in Flutter + +We have not yet added support for embedding non-Flutter components — Custom Elements, React components, Angular components — in a Flutter web app, but we intend to explore this in the future. One possible avenue is to use the [platform view](https://master-docs-flutter-io.firebaseapp.com/flutter/dart-ui/SceneBuilder/addPlatformView.html) for dropping foreign content into a Flutter web app. One important aspect to consider is what kind of effect foreign content may have on the performance and correctness of apps. Because non-Flutter components are likely to contain arbitrary CSS in them, as mentioned above, it can be problematic. More research is required. + +## Portability + +Our goal is to make as much of the framework portable to the Web as possible. However, that does not mean that an arbitrary Flutter app will run on the Web with no code changes. A Flutter web app is still a web app; it is sandboxed in the browser and can only do what the web browser allows. For example, if your Flutter app uses a native plugin that does not have a Web implementation, such as ARCore, you won’t be able to run the app on the Web. Similarly, there is no direct access to the file system or low-level network. + +## Current status + +We built enough of the Web Engine to render most of the Flutter Gallery. We have not ported Cupertino widgets, but all of the Material widgets, Material Theming, as well as the Shrine and Contact Profile demo apps run on the Web. + +<YoutubeEmbed id="5IrPi2Eo-xM" title="hummingbird screen recording" fullwidth="true"/> + + +## Where is the source code? + +We plan to open source this project soon, and are excited about sharing it with the open source community. This project started as an exploration inside Google’s internal source tree. We intend to move development to GitHub as soon as our code stabilizes and we’ve had a chance to disentangle it from our internal infrastructure. In the meantime, do not be surprised if you see Web-related pull requests under the github.com/flutter organization! + +## Conclusion + +I hope this post gives you an idea of the challenges we are solving to make Flutter run well on the Web. We welcome your thoughts and ideas. + +Stay tuned for Google I/O 2019! \ No newline at end of file diff --git a/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/103SErV-PT46HyU9nmuwd7g.webp b/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/103SErV-PT46HyU9nmuwd7g.webp new file mode 100644 index 0000000000..8fb81f3c04 Binary files /dev/null and b/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/103SErV-PT46HyU9nmuwd7g.webp differ diff --git a/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/1I0xzMeuSQHI_vB0x28IucQ.webp b/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/1I0xzMeuSQHI_vB0x28IucQ.webp new file mode 100644 index 0000000000..efea31e753 Binary files /dev/null and b/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/1I0xzMeuSQHI_vB0x28IucQ.webp differ diff --git a/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/1Qyyvx52AjpBRuI8MeZ7cAg.webp b/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/1Qyyvx52AjpBRuI8MeZ7cAg.webp new file mode 100644 index 0000000000..e9a2d640ef Binary files /dev/null and b/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/1Qyyvx52AjpBRuI8MeZ7cAg.webp differ diff --git a/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/1l1WjOu97J6hfDTO0KgzKlA.webp b/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/1l1WjOu97J6hfDTO0KgzKlA.webp new file mode 100644 index 0000000000..b37a623063 Binary files /dev/null and b/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/1l1WjOu97J6hfDTO0KgzKlA.webp differ diff --git a/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/1vD6H6j8WKFxS5RJf8Z0EDA.webp b/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/1vD6H6j8WKFxS5RJf8Z0EDA.webp new file mode 100644 index 0000000000..6ca1185c8c Binary files /dev/null and b/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/1vD6H6j8WKFxS5RJf8Z0EDA.webp differ diff --git a/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/1yuiVVfFOuPnrsHcUx7xf_Q.webp b/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/1yuiVVfFOuPnrsHcUx7xf_Q.webp new file mode 100644 index 0000000000..4aff0d19eb Binary files /dev/null and b/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/1yuiVVfFOuPnrsHcUx7xf_Q.webp differ diff --git a/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/1zAjKICKgCTiEiiMTou9MJQ.gif b/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/1zAjKICKgCTiEiiMTou9MJQ.gif new file mode 100644 index 0000000000..bfa48c776e Binary files /dev/null and b/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/images/1zAjKICKgCTiEiiMTou9MJQ.gif differ diff --git a/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/index.md b/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/index.md new file mode 100644 index 0000000000..5c04df5433 --- /dev/null +++ b/sites/www/content/blog/i-o-pinball-powered-by-flutter-and-firebase/index.md @@ -0,0 +1,240 @@ +--- +title: "I/O Pinball Powered by Flutter and Firebase" +description: "Take Flutter game development to the next level" +publishDate: 2022-05-10 +author: verygoodopensource +image: images/1yuiVVfFOuPnrsHcUx7xf_Q.webp +category: events +layout: blog +--- + +<DashImage figure src="images/1yuiVVfFOuPnrsHcUx7xf_Q.webp" /> + + +For Google I/O, we teamed up with the Flutter team to reimagine a classic pinball game built using Flutter and Firebase. Here’s how we brought [I/O Pinball](https://pinball.flutter.dev/) to life on the web with the help of the Flame game engine. + +## Game development essentials + +The Flutter framework is a great choice for building games driven by user interaction, such as puzzles and word games. When it comes to games that use a game loop, [Flame](https://docs.flame-engine.org/), a 2D game engine built on top of Flutter, can be a helpful tool. I/O Pinball uses Flame’s out-of-the-box features, such as animations, physics, collision detection, and more, while also leveraging the infrastructure of the Flutter framework. If you can build an app with Flutter, you already have the foundation you need to build games with Flame. + +<DashImage figure src="images/1Qyyvx52AjpBRuI8MeZ7cAg.webp" /> + + +## Game loop + +In conventional apps, screens are usually visually static until there is an event or interaction from the user. With games, the inverse is true — the UI is rendered continuously and the state of the game constantly changes. Flame provides a game widget, which internally manages the game loop so that the UI is constantly rendering in a performant way. The `Game` class contains the implementation of the game components and logic, which is passed to the `GameWidget` in the widget tree. In I/O Pinball, the game loop reacts to the position and state of the ball on the playfield and applies the necessary effects if the ball collides with an object or falls out of play. + +``` +@override +void update(double dt) { + super.update(dt); + + final direction = -parent.body.linearVelocity.normalized(); + angle = math.atan2(direction.x, -direction.y); + size = (_textureSize / 45) * + parent.body.fixtures.first.shape.radius; +} +``` + + +## Rendering a 3D space with 2D components + +One of the challenges of building I/O Pinball was figuring out how to create a 3D effect using only 2D elements. Components are ordered to determine how they render on the screen. For example, as the ball is launched up the ramp, the ball’s order increases, so that it appears to be on top of the ramp. + +<DashImage figure src="images/103SErV-PT46HyU9nmuwd7g.webp" /> + + +The ball, plunger, both flippers, and the Chrome Dino are elements with a dynamic body, which are affected by the world’s physics. The ball also changes size depending on its position on the board. As the ball moves to the top of the board, it shrinks in size to appear farther away from the user’s perspective. In addition, the gravity on the ball is adjusted to take into account the angle of the pinball machine, so that the ball falls faster on an incline. + +``` +/// Scales the ball's body and sprite according to its position on the board. +class BallScalingBehavior extends Component with ParentIsA<Ball> { + @override + void update(double dt) { + super.update(dt); + final boardHeight = BoardDimensions.bounds.height; + const maxShrinkValue = BoardDimensions.perspectiveShrinkFactor; + + final standardizedYPosition = parent.body.position.y + (boardHeight / 2); + final scaleFactor = maxShrinkValue + + ((standardizedYPosition / boardHeight) * (1 - maxShrinkValue)); + +parent.body.fixtures.first.shape.radius = (Ball.size.x / 2) * scaleFactor; + +final ballSprite = parent.descendants().whereType<SpriteComponent>(); + if (ballSprite.isNotEmpty) { + ballSprite.single.scale.setValues( + scaleFactor, + scaleFactor, + ); + } + } +} +``` + + +## Physics with Forge 2D + +I/O Pinball heavily relies upon the [`forge2d`](https://pub.dev/packages/forge2d) package maintained by the Flame team. This package ports the open source [Box2D physics engine](https://box2d.org/) into Dart so that it can be easily integrated with Flutter. We used `forge2d` to power the physics of the game, for example collision detection between objects (`Fixtures`) on the playfield. + +`forge2D` allows us to listen to when collisions between `Fixtures` occur. We then add `ContactCallbacks` to `Fixtures` to be notified when contact happens between two elements. For example, when the ball (which has a `Fixture` with a `CircleShape`) comes in contact with a bumper (which has a `Fixture` with an `EllipseShape`), the score increases. On these callbacks we can set exactly where contact begins and ends, so that when two elements come in contact with another, a collision occurs. + +``` +@override +Body createBody() { + final shape = CircleShape()..radius = size.x / 2; + final bodyDef = BodyDef( + position: initialPosition, + type: BodyType.dynamic, + userData: this, + ); + + return world.createBody(bodyDef) + ..createFixtureFromShape(shape, 1); +} +``` + + +## Sprite sheet animations + +There are a few elements on the pinball playfield, such as Android, Dash, Sparky, and Chrome Dino, which are animated. For these, we used sprite sheets, which are included in the Flame engine with the `SpriteAnimationComponent`. For each element, we had a file with the image in various orientations, the number of frames in the file, and the time between frames. Using this data, the `SpriteAnimationComponent` in Flame compiles all of the images together on a loop so that the element appears animated. + +<DashImage figure src="images/1l1WjOu97J6hfDTO0KgzKlA.webp" alt="Sprite sheet example" caption="Sprite sheet example" /> + + +``` +final spriteSheet = gameRef.images.fromCache( + Assets.images.android.spaceship.animatronic.keyName, +); + +const amountPerRow = 18; +const amountPerColumn = 4; +final textureSize = Vector2( + spriteSheet.width / amountPerRow, + spriteSheet.height / amountPerColumn, +); +size = textureSize / 10; + +animation = SpriteAnimation.fromFrameData( + spriteSheet, + SpriteAnimationData.sequenced( + amount: amountPerRow * amountPerColumn, + amountPerRow: amountPerRow, + stepTime: 1 / 24, + textureSize: textureSize, + ), +); +``` + + +## A closer look at the I/O Pinball Codebase + +## Leaderboard with live results from Firebase + +The I/O Pinball leaderboard displays the top scores of players around the world in real time. Users can also share their scores to Twitter and Facebook. We use Firebase [Cloud Firestore](https://firebase.google.com/docs/firestore) to track the top ten scores and fetch them to display on the leaderboard. When a new score is written to the leaderboard, a [Cloud Function](https://firebase.google.com/docs/functions) resorts the scores in descending order and removes any scores not currently in the top ten. + +<DashImage figure src="images/1vD6H6j8WKFxS5RJf8Z0EDA.webp" /> + + +``` +/// Acquires top 10 [LeaderboardEntryData]s. +Future<List<LeaderboardEntryData>> fetchTop10Leaderboard() async { + try { + final querySnapshot = await _firebaseFirestore + .collection(_leaderboardCollectionName) + .orderBy(_scoreFieldName, descending: true) + .limit(_leaderboardLimit) + .get(); + final documents = querySnapshot.docs; + return documents.toLeaderboard(); + } on LeaderboardDeserializationException { + rethrow; + } on Exception catch (error, stackTrace) { + throw FetchTop10LeaderboardException(error, stackTrace); + } +} +``` + + +## Building for the web + +It can be easier to build a responsive game compared to a conventional app. The pinball playfield simply needs to scale to the size of the device. For I/O Pinball, we zoom based on the size of your device on a fixed ratio. This ensures that the coordinate system is always the same, no matter the display size, which is important to ensure that components appear and interact consistently across devices. + +I/O Pinball also adapts to a mobile or desktop browser. On a mobile browser, users can tap the launch button to begin play, as well as tap the left and right sides of the screen to control the corresponding flippers. On a desktop browser, users can use the keyboard to launch the ball and control the flippers. + +## Codebase architecture + +The pinball codebase follows a layered architecture, with each feature in its own folder. The game logic is also separated from the visual components in this project. This ensures that we could easily update visual elements independently of the game logic and vice versa. + +The theme of pinball varies depending on which character a user selects before beginning the game. Theming is controlled with the `CharacterThemeCubit` class. Depending on the character selection, the ball color, background, and other elements are updated. + +<DashImage figure src="images/1I0xzMeuSQHI_vB0x28IucQ.webp" /> + + +``` +/// {@template character_theme} +/// Base class for creating character themes. +/// +/// Character specific game components should have a getter specified here to +/// load their corresponding assets for the game. +/// {@endtemplate} +abstract class CharacterTheme extends Equatable { + /// {@macro character_theme} + const CharacterTheme(); + +/// Name of character. + String get name; + +/// Asset for the ball. + AssetGenImage get ball; + +/// Asset for the background. + AssetGenImage get background; + +/// Icon asset. + AssetGenImage get icon; + +/// Icon asset for the leaderboard. + AssetGenImage get leaderboardIcon; + +/// Asset for the the idle character animation. + AssetGenImage get animation; + +@override + List<Object> get props => [ + name, + ball, + background, + icon, + leaderboardIcon, + animation, + ]; +} +``` + + +I/O Pinball game state is handled with [`flame_bloc`](https://pub.dev/packages/flame_bloc), a package that bridges blocs with Flame components. For example, we use `flame_bloc` to keep track of the number of rounds left to play, any bonuses achieved through the game, as well as the current game score. In addition, there is a widget at the top of the widget tree that contains logic for the loading page, including instructions for how to play the game. We also follow the [behavior pattern](https://en.wikipedia.org/wiki/Behavioral_pattern) to encapsulate and isolate certain elements of a game feature based on its components. For example, the bumpers play a sound when hit by the ball, so we implemented the `BumperNoiseBehavior` class to handle this. + +``` +class BumperNoiseBehavior extends ContactBehavior { + @override + void beginContact(Object other, Contact contact) { + super.beginContact(other, contact); + readProvider<PinballPlayer>().play(PinballAudio.bumper); + } +} +``` + + +The codebase also contains comprehensive unit, widget, and golden tests. At times, testing the game posed a few challenges due to the fact that single components could have multiple responsibilities, which made them hard to test in isolation. As a result, we ended up defining patterns to better isolate and test components. We also incorporated improvements into the [`flame_test`](https://pub.dev/packages/flame_test) package. + +## Component sandbox + +This project relies heavily on Flame components to bring the pinball experience to life. The codebase comes with a component sandbox, which is similar to a [UI component gallery](https://gallery.flutter.dev/#/). This is a helpful tool when developing games because it allows you to develop the game components in isolation and ensure that they look and behave as expected before integrating them into the game. + +<DashImage figure src="images/1zAjKICKgCTiEiiMTou9MJQ.gif" /> + + +## What’s next + +See if you can get a high score in [I/O Pinball](https://pinball.flutter.dev/)! The code is open source in [this GitHub repo](https://github.com/flutter/pinball). Keep an eye on the leaderboard and be sure to share your score on social media! \ No newline at end of file diff --git a/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/05DO-hJz2NO5xPsMT.webp b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/05DO-hJz2NO5xPsMT.webp new file mode 100644 index 0000000000..ba294e335f Binary files /dev/null and b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/05DO-hJz2NO5xPsMT.webp differ diff --git a/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/09ldHbXX7XFfWu-op.webp b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/09ldHbXX7XFfWu-op.webp new file mode 100644 index 0000000000..d158607e8a Binary files /dev/null and b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/09ldHbXX7XFfWu-op.webp differ diff --git a/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0BqpEt11-35wDedUr.webp b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0BqpEt11-35wDedUr.webp new file mode 100644 index 0000000000..54af72b87f Binary files /dev/null and b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0BqpEt11-35wDedUr.webp differ diff --git a/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0PSy8K56xJKtZ_7AX.webp b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0PSy8K56xJKtZ_7AX.webp new file mode 100644 index 0000000000..2ac2974fd0 Binary files /dev/null and b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0PSy8K56xJKtZ_7AX.webp differ diff --git a/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0SKqIROqsnOclxsM2.webp b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0SKqIROqsnOclxsM2.webp new file mode 100644 index 0000000000..628bc94c0c Binary files /dev/null and b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0SKqIROqsnOclxsM2.webp differ diff --git a/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0W4ladsI7F9pgXph1.webp b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0W4ladsI7F9pgXph1.webp new file mode 100644 index 0000000000..7975e77402 Binary files /dev/null and b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0W4ladsI7F9pgXph1.webp differ diff --git a/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0_Eo60HUKPSvvx_p_.webp b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0_Eo60HUKPSvvx_p_.webp new file mode 100644 index 0000000000..d46971d9e1 Binary files /dev/null and b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0_Eo60HUKPSvvx_p_.webp differ diff --git a/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0j9j0D-2k7v2_NhAX.webp b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0j9j0D-2k7v2_NhAX.webp new file mode 100644 index 0000000000..c95db1deeb Binary files /dev/null and b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0j9j0D-2k7v2_NhAX.webp differ diff --git a/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0k2lkGMPnru3RgKI6.webp b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0k2lkGMPnru3RgKI6.webp new file mode 100644 index 0000000000..da27e763aa Binary files /dev/null and b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0k2lkGMPnru3RgKI6.webp differ diff --git a/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0nA3zAVD8v8ArX5ai.webp b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0nA3zAVD8v8ArX5ai.webp new file mode 100644 index 0000000000..077f7947f2 Binary files /dev/null and b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/0nA3zAVD8v8ArX5ai.webp differ diff --git a/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/19im6A1aef_HaADS0nvMkUg.webp b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/19im6A1aef_HaADS0nvMkUg.webp new file mode 100644 index 0000000000..91e84aedde Binary files /dev/null and b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/19im6A1aef_HaADS0nvMkUg.webp differ diff --git a/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/1BobyRIQmaCBwnw3bYBTe6w.webp b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/1BobyRIQmaCBwnw3bYBTe6w.webp new file mode 100644 index 0000000000..9902295487 Binary files /dev/null and b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/images/1BobyRIQmaCBwnw3bYBTe6w.webp differ diff --git a/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/index.md b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/index.md new file mode 100644 index 0000000000..f0c28e1ec2 --- /dev/null +++ b/sites/www/content/blog/improving-flutter-with-your-opinion-q4-2019-survey-results/index.md @@ -0,0 +1,144 @@ +--- +title: "Improving Flutter with your opinion — Q4 2019 survey results" +description: "Written by JaYoung Lee and Tao Dong" +publishDate: 2020-02-11 +author: jayoung-lee +image: images/0W4ladsI7F9pgXph1.webp +category: announcements +layout: blog +--- + +## **Improving Flutter with your opinion — Q4 2019 survey results** + +<DashImage figure src="images/0W4ladsI7F9pgXph1.webp" /> + + +The Flutter UX research team ran the 7th quarterly survey in November of 2019 and received 6,343 responses. Thank you for participating — each and every response matters. As always, we are back to share our findings. + +First and foremost, we found that 93% of respondents are positively satisfied with Flutter, meeting our 2019 goal of maintaining 90% or higher satisfaction rate! We’re delighted that even as Flutter’s active developer base grows, we’ve continued to make developers happy. + +Here are the top 5 issues mentioned in the responses. For more information, please read the detailed report below. + +1. **Cupertino widgets:** About 20% of all developers are (1) considering or using Cupertino widgets (Flutter’s iOS-styled widgets) for production apps and (2) think that it is *very* or *extremely* important to replicate the iOS look and feel with the Cupertino widgets. + +1. **GitHub issues:** Only 15% of the survey respondents have filed at least one issue on the flutter/flutter repo GitHub, and only 23% have voted on existing issues. + +1. **CI/CD:** 20% of respondents have used Continuous Integration and Delivery (CI/CD) services for their Flutter projects, and the top three preferred services were: Codemagic, GitLab CI/CD, and Jenkins. 60% to 74% of developers were positively satisfied with these services. + +1. **Hot reload:** 70% of respondents think that hot reload is working as expected more than 90% of the time. When hot reload doesn’t work, 23% of developers *seldom* or *never* know why. + +1. **Dart String API:** 17% of developers have either heard about or encountered an issue with unicode characters that require two bytes to express, such as emojis, diacriticals, etc. About 10% of those who experienced the issue could not release their app, because of it. + +Findings from each topic were shared with the Flutter/Dart team for future planning. Please see the sections below for further details. + +## Cupertino widgets + +In previous surveys, the Cupertino widget library (which enables the iOS design language in Flutter) have scored lower for satisfaction compared to other subsystems. Only around 70% of developers were *very satisfied* or *somewhat satisfied* with Cupertino widgets, while over 80% of developers are positively satisfied with other subsystems, such as Material widgets or the core framework. Note that the satisfaction rate was calculated after excluding respondents who selected “I have no experience with this.” + +<DashImage figure src="images/09ldHbXX7XFfWu-op.webp" alt="*Cupertino widgets have lower satisfaction rate than other Flutter subsystems*" caption="*Cupertino widgets have lower satisfaction rate than other Flutter subsystems*" /> + + +We also noticed that many respondents were indifferent about Cupertino widgets (20% were neither satisfied nor dissatisfied), even when excluding 41% of all respondents who selected “I have no experience with Cupertino widgets.” + +<DashImage figure src="images/0j9j0D-2k7v2_NhAX.webp" alt="*20% of developers are neither satisfied nor dissatisfied with Cupertino widgets*" caption="*20% of developers are neither satisfied nor dissatisfied with Cupertino widgets*" /> + + +We wanted to learn why the Cupertino widget set has a comparatively low satisfaction score. From follow-up questions, we learned that only 53% of respondents use Cupertino widgets seriously, either in their production apps, or in evaluating them for potential production use. Others said that they use Cupertino widgets just for fun, for experimenting, or don’t use them at all. This was an important data point, because we wanted to understand the specific pain points when using Cupertino widgets in production, and how big an impediment this was for Flutter developers. + +We learned that about 70% of those who use Cupertino widgets *seriously* think that it is *very* or *extremely important* to replicate the appearance and behavior of native iOS design. If we take all developers into account (including those who selected “I have no experience with Cupertino widgets”), this is about 20% of all developers. It tells us that, while Cupertino widgets is a weak area, the benefit of prioritizing this area applies to only a small group of developers. + +We appreciate receiving detailed bug reports where you identify fidelity differences, as well as pull requests that fix reported issues, so that we can focus our effort. Recent work includes iOS 13 dark mode. As we make more improvements around Cupertino widgets, you can track progress in our [changelog](https://github.com/flutter/flutter/wiki/Changelog). + +<DashImage figure src="images/1BobyRIQmaCBwnw3bYBTe6w.webp" alt="*About 70% of developers who use Cupertino widgets seriously think that it is very or extremely important to replicate native iOS look & feel*" caption="*About 70% of developers who use Cupertino widgets seriously think that it is very or extremely important to replicate native iOS look & feel*" /> + + +## GitHub issues + +In addition to quarterly surveys, we use [GitHub issues](https://github.com/flutter/flutter/issues) to solicit feedback and track issues with Flutter in real time. Although many developers file issues and converse with the team on GitHub, we wondered how many of the survey respondents overlap with these GitHub users. + +It turns out that only 15% of the survey respondents have filed at least one issue on GitHub, and only 23% have voted on existing issues. Almost a third (29%) of respondents have never filed or voted on an issue. + +<DashImage figure src="images/0SKqIROqsnOclxsM2.webp" alt="*Only 15% of respondents had experience filing GitHub issues, and 23% have upvoted for existing issues*" caption="*Only 15% of respondents had experience filing GitHub issues, and 23% have upvoted for existing issues*" /> + + +This confirms that, for many developers, the quarterly user survey might be the only avenue to give direct feedback to the team. Thus, the team will continue to field surveys and monitor both sources to understand developer needs. + +## CI/CD + +There are several CI/CD (continuous integration and delivery) service options for Flutter. We want to make them more friendly for Flutter developers, but we do not know which pain points are most critical. We asked whether respondents have used CI/CD with Flutter in the past. Of the 20% of developers who have used CI/CD, we asked which service they used, and how satisfied they are. + +<DashImage figure src="images/0k2lkGMPnru3RgKI6.webp" alt="*20% of the developers have implemented CI/CD with Flutter*" caption="*20% of the developers have implemented CI/CD with Flutter*" /> + + +As you can see in the graph below, Flutter developers use various CI/CD services, of which the top three are: Codemagic, GitLab CI/CD, and Jenkins. All three had less than 74% satisfaction level, which is not bad, but lower than what we see in other measures of Flutter satisfaction. + +<DashImage figure src="images/19im6A1aef_HaADS0nvMkUg.webp" alt="*Satisfaction with various CI/CD services were between 60% and 74% (Note: the services with less than 150 developers are greyed out*" caption="*Satisfaction with various CI/CD services were between 60% and 74% (Note: the services with less than 150 developers are greyed out*" /> + + +From the open-ended question “What is your current CI/CD setup for your Flutter app, and what are the pain points?”, we collected 462 comments. After manually categorizing them, we found 6 common themes that cause pain: missing functionality, difficult set up, configuring the iOS build, speed, stability, and ease of publishing. + +<DashImage figure src="images/0PSy8K56xJKtZ_7AX.webp" alt="*More than 60 respondents commented about missing functionality and difficulty in setup as their pain points when using CI/CD services with Flutter*" caption="*More than 60 respondents commented about missing functionality and difficulty in setup as their pain points when using CI/CD services with Flutter*" /> + + +Based on the data, we are brainstorming on ways to address these issues. The first proposal suggests officially maintaining several CI/CD integration configurations. The second proposal suggests providing tools to simplify iOS building, managing certificates, and so on. The third proposal suggests collaborating with Firebase to provide better integration with the Firebase test lab. Although the decision has not been finalized, the team will use feedback from the survey to ensure that we offer what is actually wanted and needed. + +## Hot reload + +Feedback from previous surveys and user studies have shown that developers had some difficulty understanding hot reload’s limitations — when *doesn’t* hot reload update the UI? + +The Flutter team has plans to make hot reload easier to understand and more trustworthy. In this survey, we wanted to establish a baseline against which the effectiveness of future improvements can be measured. + +According to the survey, 70% of respondents think that hot reload is working as expected more than 90% of the time. When it doesn’t work, 23% of developers *seldom* or *never* know why. Even 20% of those who have used Flutter for over a year seldom or never know why it doesn’t work. + +<DashImage figure src="images/05DO-hJz2NO5xPsMT.webp" alt="*About 70% of respondents think that hot reload work as expected in >90% of the cases*" caption="*About 70% of respondents think that hot reload work as expected in >90% of the cases*" /> + + +<DashImage figure src="images/0nA3zAVD8v8ArX5ai.webp" alt="*20% to 27% of developers seldom or never know why hot reload doesn’t work (23% overall)*" caption="*20% to 27% of developers seldom or never know why hot reload doesn’t work (23% overall)*" /> + + +Because hot reload touches almost every aspect of Flutter, from the IDE to the Dart VM, we held a cross-team workshop to analyze open-ended comments survey respondents submitted; we identified both known and unexpected hot reload limitations that developers ran into in the wild. + +We will take the following actions in the next few months: + +* Remove the StatelessWidget to StatefulWidget hot reload restriction +[https://github.com/flutter/flutter/pull/48932](https://github.com/flutter/flutter/pull/48932) + +* Verify the problem that hot reload doesn’t reflect asset changes (for example, fonts or images) +[https://github.com/flutter/flutter/issues/49230](https://github.com/flutter/flutter/issues/49230) + +* Look into the issue about doing a hot reload while using the debugger +[https://github.com/flutter/flutter-intellij/issues/2241](https://github.com/flutter/flutter-intellij/issues/2241) + +* Take a look at state management packages and how using them affects hot reload behavior +[https://github.com/flutter/website/issues/3566](https://github.com/flutter/website/issues/3566) +[https://github.com/flutter/flutter-intellij/issues/4310](https://github.com/flutter/flutter-intellij/issues/4310) + +* Document hot reload limitations with regard to native code changes, routing, and package changes +[https://github.com/flutter/website/issues/2920](https://github.com/flutter/website/issues/2920) + +* Surface VM connection status in the IDE +[https://github.com/flutter/flutter-intellij/issues/4309](https://github.com/flutter/flutter-intellij/issues/4309) + +We also have plans to allow the IDE to suggest hot restart after non-reloadable code changes have been made. However, more investigations are needed to minimize false positives and ensure an unobtrusive developer experience. + +## String API + +Dart’s String API doesn’t support manipulating [extended grapheme clusters](https://github.com/dart-lang/language/issues/34) such as 👍 😀 and 🇩🇰, out of the box. To address this limitation, the Dart team recently published an experimental package called [characters](https://pub.dev/packages/characters). However, it’s unclear whether this solution is sufficient and, in this survey, we wanted to find out. + +17% of developers have heard about or experienced the extended grapheme clusters issue. About 10% of those who experienced the issue could not release their app because of it. + +<DashImage figure src="images/0BqpEt11-35wDedUr.webp" alt="*17% of developers have heard about or ran into the extended grapheme clusters issue*" caption="*17% of developers have heard about or ran into the extended grapheme clusters issue*" /> + + +When we asked whether developers prefer built-in grapheme support in the String API, which would require breaking changes, or the current package-based solution, the opinions came to a nearly even split. Nonetheless, we are surprised by the number of developers who are willing to consider breaking the String API in exchange for first-class support of extended grapheme clusters, as you can see below. + +<DashImage figure src="images/0_Eo60HUKPSvvx_p_.webp" alt="*The opinion was almost an even split. To our surprise, many developers preferred the breaking change option (option 1) over the package option (option 2).*" caption="*The opinion was almost an even split. To our surprise, many developers preferred the breaking change option (option 1) over the package option (option 2).*" /> + + +The results from this survey will help the team evaluate the options and tradeoffs further this quarter. We’ll continue taking feedback from the community through discussions on related bugs and with more targeted user studies. + +## And finally.. + +Flutter’s quarterly user survey is an invaluable way for us to understand your experience with Flutter. Your feedback directly affects where we focus our efforts. Please continue to support the surveys, and also consider [signing up](http://flutter.dev/research-signup) for future UX studies. + +Thank you! \ No newline at end of file diff --git a/sites/www/content/blog/improving-flutters-error-messages/images/0X_nD364tctKThHvy.webp b/sites/www/content/blog/improving-flutters-error-messages/images/0X_nD364tctKThHvy.webp new file mode 100644 index 0000000000..d57e754847 Binary files /dev/null and b/sites/www/content/blog/improving-flutters-error-messages/images/0X_nD364tctKThHvy.webp differ diff --git a/sites/www/content/blog/improving-flutters-error-messages/images/0_b3-Pr2bUFSsbAUM.webp b/sites/www/content/blog/improving-flutters-error-messages/images/0_b3-Pr2bUFSsbAUM.webp new file mode 100644 index 0000000000..d60abd3096 Binary files /dev/null and b/sites/www/content/blog/improving-flutters-error-messages/images/0_b3-Pr2bUFSsbAUM.webp differ diff --git a/sites/www/content/blog/improving-flutters-error-messages/images/0evHu5BfK5pfBSCz3.webp b/sites/www/content/blog/improving-flutters-error-messages/images/0evHu5BfK5pfBSCz3.webp new file mode 100644 index 0000000000..0124f0ebd7 Binary files /dev/null and b/sites/www/content/blog/improving-flutters-error-messages/images/0evHu5BfK5pfBSCz3.webp differ diff --git a/sites/www/content/blog/improving-flutters-error-messages/images/0uct3j7acy9KVMqi2.webp b/sites/www/content/blog/improving-flutters-error-messages/images/0uct3j7acy9KVMqi2.webp new file mode 100644 index 0000000000..7da566e69c Binary files /dev/null and b/sites/www/content/blog/improving-flutters-error-messages/images/0uct3j7acy9KVMqi2.webp differ diff --git a/sites/www/content/blog/improving-flutters-error-messages/images/1FHiGbc69QAdOuRGDezhc1A.webp b/sites/www/content/blog/improving-flutters-error-messages/images/1FHiGbc69QAdOuRGDezhc1A.webp new file mode 100644 index 0000000000..f6436f0d37 Binary files /dev/null and b/sites/www/content/blog/improving-flutters-error-messages/images/1FHiGbc69QAdOuRGDezhc1A.webp differ diff --git a/sites/www/content/blog/improving-flutters-error-messages/images/1Vz1dqvpOYqnFOIEVIZuVww.webp b/sites/www/content/blog/improving-flutters-error-messages/images/1Vz1dqvpOYqnFOIEVIZuVww.webp new file mode 100644 index 0000000000..47b41277ff Binary files /dev/null and b/sites/www/content/blog/improving-flutters-error-messages/images/1Vz1dqvpOYqnFOIEVIZuVww.webp differ diff --git a/sites/www/content/blog/improving-flutters-error-messages/images/1l8l2xWisc5Mtc6xzFaucmg.webp b/sites/www/content/blog/improving-flutters-error-messages/images/1l8l2xWisc5Mtc6xzFaucmg.webp new file mode 100644 index 0000000000..f00fca0bb9 Binary files /dev/null and b/sites/www/content/blog/improving-flutters-error-messages/images/1l8l2xWisc5Mtc6xzFaucmg.webp differ diff --git a/sites/www/content/blog/improving-flutters-error-messages/images/1l8nn2JJ5rSQqfW2col87NQ.webp b/sites/www/content/blog/improving-flutters-error-messages/images/1l8nn2JJ5rSQqfW2col87NQ.webp new file mode 100644 index 0000000000..886e1f0875 Binary files /dev/null and b/sites/www/content/blog/improving-flutters-error-messages/images/1l8nn2JJ5rSQqfW2col87NQ.webp differ diff --git a/sites/www/content/blog/improving-flutters-error-messages/index.md b/sites/www/content/blog/improving-flutters-error-messages/index.md new file mode 100644 index 0000000000..ce092af193 --- /dev/null +++ b/sites/www/content/blog/improving-flutters-error-messages/index.md @@ -0,0 +1,108 @@ +--- +title: "Improving Flutter’s Error Messages" +description: "Can visual design be applied to error messages? We found that it drastically improves the usability of error messages in Flutter." +publishDate: 2019-09-09 +author: taodong +image: images/1l8l2xWisc5Mtc6xzFaucmg.webp +category: announcements +layout: blog +--- + +When you write computer programs, it’s inevitable that errors are introduced, even for the most experienced developer. To resolve an error, the first step is often reading the error message that’s displayed in the console. But, [research](https://www.hindawi.com/journals/ahci/2010/602570/abs/) shows that programmers, especially novices, struggle to make sense of error messages, never mind take action on them. + +Admittedly, when it comes to helping you, the developer, recover from errors, Flutter hasn’t done a great job. The console output from an error is typically verbose, and it’s often unclear how an error can be traced back to a particular place in your code. Lately, we’ve been working on addressing both problems. In this post, I’m going to introduce our first attempt at increasing the signal-to-noise ratio of Flutter’s runtime error messages, and describe the research we did to arrive at the current solution. + +## Introducing structured error messages + +In the latest release of the Flutter plugin for IntelliJ/Android Studio and the extension for VS Code, we shipped a new feature that displays error messages in a rich, yet concise, format. The logging console can now display error messages with the following improvements: + +1. Highlighting the summary of the error in red + +1. Adding whitespace between sections to make the message more scannable + +1. Calling out the hint in the message, if available, for resolving the error + +1. Collapsing long lists and trees in the message + +The following screenshot shows these four improvements, identified with blue circles, in the message that’s generated by the layout overflow error: + +<DashImage figure src="images/1l8l2xWisc5Mtc6xzFaucmg.webp" alt="An example of the new structured error message" caption="An example of the new structured error message" /> + + +Note that everything fits in one screen, making it possible to get a general understanding of the problem at a glance. The reduction in noise is done without deleting any information that might be useful for cases where more detail is important in order to fix the error. + +In contrast, the original error message is a lot denser and lacks structure, so it’s difficult to find the information that tells you how to fix the error. See the following screenshot. + +<DashImage figure src="images/1l8nn2JJ5rSQqfW2col87NQ.webp" alt="The original message for the same RenderFlex Overflow error" caption="The original message for the same RenderFlex Overflow error" /> + + +At this time, we refactored about 85% of the error messages in the Flutter framework, to take advantage of this new presentation. Improvements to the rest of the error messages will happen over time. We also plan to refine the general presentation of error messages based on user feedback. + +## How did we get here? + +The usability of error messages is a multifaceted problem. The content, structure, and presentation of an error message all contribute to your ability to make sense of it and fix the error. We know that error messages in Flutter generally provide helpful content, but the usefulness of the content is undermined by the overwhelming amount of information that you had to sift through. + +As an example, let’s take a look at the “No Material widget found” error. This error occurs when a widget expects a Material widget in its ancestor tree but one isn’t found. The message for this error was so hard to figure out that a user [sought help](https://stackoverflow.com/questions/43947552/using-textfield-throws-no-material-widget-found-error) from StackOverflow. The accepted answer pointed out the key information already in the message, but the user either couldn’t find the needed information or didn’t understand it. No new information was given. + +<DashImage figure src="images/0X_nD364tctKThHvy.webp" alt="A question posted to StackOverflow about the “No Material widget found” error" caption="A question posted to StackOverflow about the “No Material widget found” error" /> + + +### The new way of presenting error messages + +The question posted on StackOverflow is revealing. It shows that not all information in an error message is equal. This question also inspired us to think about how we could make useful information more noticeable, which meant cutting down on visual clutter. Applying [theories of visual perception](https://www.interaction-design.org/literature/article/laws-of-proximity-uniform-connectedness-and-continuation-gestalt-principles-2) and [techniques in UX design](https://www.nngroup.com/articles/progressive-disclosure), we came up with three variants for that error message: colors, spaces, and ellipses. + +To create the colors variant, we analyzed the relative importance of different pieces of information in the message and came up with a simple color coding scheme: + +* Display a one-line summary of the error in red. + +* Display the object that the error is associated with in blue. + +* Display the detail that isn’t usually needed in gray. + +<DashImage figure src="images/1Vz1dqvpOYqnFOIEVIZuVww.webp" alt="The colors variant of the message for the “Missing Material” error" caption="The colors variant of the message for the “Missing Material” error" /> + + +The next variant is called spaces. We added whitespace to the error message to separate it into sections. We also added section headings such as Explanation and Potential Fix to make the message easier to skim. + +<DashImage figure src="images/0uct3j7acy9KVMqi2.webp" alt="The spaces variant of the message for the “Missing Material” error" caption="The spaces variant of the message for the “Missing Material” error" /> + + +Last, the ellipses variant applies the [progressive disclosure technique](https://www.nngroup.com/articles/progressive-disclosure) in error message presentation and leverages the capabilities of a modern IDE. For example, we collapsed the TextField widget’s parameter list and the widget’s ancestors after the first three are displayed. To see the rest of the list, just click the ellipsis that follows the last parameter or ancestor. The resulting error message is considerably shorter than the original, increasing the likelihood that your attention is drawn to the higher-level elements in the message, all at a glance. + +<DashImage figure src="images/1FHiGbc69QAdOuRGDezhc1A.webp" alt="The ellipses variant of the message for the “Missing Material” error" caption="The ellipses variant of the message for the “Missing Material” error" /> + + +We had good reason to believe that these variants would help the user, but we weren’t sure whether we could justify the cost to implement them. To make those presentations real, we needed to make the Flutter framework send structured error data to the IDE, so the IDE can style different parts (e.g., summary, detail, and hints) appropriately and collapse less important information. We asked ourselves if the potential improvement to the developer experience was worth the effort to refactor Flutter’s error API *and* the hundreds of errors that were already written. So, we decided to run an experiment, to find out how large the upside is of having structured error messages. + +### The experiment + +To compare the usability of the three variants with the original message, we conducted an online experiment using a scenario-based questionnaire. We recruited 52 Flutter users, who were randomly assigned to one of four experimental groups. The original error message was shown to the control group, while the variants were shown to the three [treatment groups](https://en.wikipedia.org/wiki/Treatment_and_control_groups), respectively. Participants were asked to describe what was wrong and how they would resolve the error in a very limited amount of time. The detailed study design can be found in the peer-reviewed [paper](https://drive.google.com/file/d/1_xNIy51yAZ7MnTHHG8SYwvvQMbt9bkMs/view) that we published earlier this year at [CHI 2019](https://chi2019.acm.org/). + +<DashImage figure src="images/0_b3-Pr2bUFSsbAUM.webp" alt="Error comprehension rates across four message variants" caption="Error comprehension rates across four message variants" /> + + +The results surprised us in a very positive way. Participants’ comprehension of the error message was substantially better in any of the three treatment groups than in the control group. In other words, the proportion of participants who correctly understood the error within the time limit was much higher when any of the variants were used rather than the original. The following chart shows that, the spaces variant outperforms the original formatting of the message by about 38 percentage points when participants were given 45 seconds in total to read the error message. + +<DashImage figure src="images/0evHu5BfK5pfBSCz3.webp" alt="Error fix rates across four message variants" caption="Error fix rates across four message variants" /> + + +Similarly, all the variants of the error message outperformed the message’s original presentation when it came to figuring out the solution. + +When participants were asked to compare the original error message and the variant they used in the experiment, they explained why they liked the variants better. The following are some examples. (Words within parentheses were added by the author of this article.) +> *“The error on the right (the ‘colors’ variant) is a huge improvement with the critical short message in red, the affected widget in blue.”* +> *“Hiding the whole Widget object reduces clutter (in the ‘ellipses’ variant), makes it easy to find the reason the error occurred.”* +> *“B (the ‘spaces’ variant) is much easier to parse. The sections are clearly broken up, so it is easy when skimming to know where to jump next.”* + +With strong evidence from the experiment, the we decided to invest in structured error messages. It was a long journey, but we’re glad that the [refactored error API](https://master-api.flutter.dev/flutter/foundation/ErrorSummary-class.html) now provides a solid foundation for future innovations, to help developers recover from errors in their Flutter projects. + +## What’s the future for error messages? + +For errors thrown by UI code, we could embed diagrams, animations, and even interactive widget trees in the message to maximize their explanatory power. We plan to start some of the more ambitious experiments in [Dart DevTools](https://flutter.dev/docs/development/tools/devtools/overview)’ console, because we aren’t constrained with the extensibility of IntelliJ and VS Code. + +### How can you help us be better? + +Not all Flutter errors were refactored to take full advantage of this new structured presentation. We’re working on the errors that affect the most Flutter users first. You can help by letting us know which error messages could be more useful via our [GitHub issue tracker](https://github.com/flutter/flutter/issues/new?labels=a:%20error%20message), or you can suggest ideas about refining the error presentation in [IntelliJ](https://github.com/flutter/flutter-intellij/issues/new) or [VS Code](https://github.com/Dart-Code/Dart-Code/issues/new). + +## Acknowledgement + +The Flutter team’s former UX Research intern [Kandarp Khandwala](http://kandarp.xyz/) made an instrumental contribution to the research described in this article. We also thank all the Flutter users who participated in the experiment. \ No newline at end of file diff --git a/sites/www/content/blog/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation/images/1Db6XWyMRM9gGhSEpHY6w7A.webp b/sites/www/content/blog/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation/images/1Db6XWyMRM9gGhSEpHY6w7A.webp new file mode 100644 index 0000000000..9b076fddf4 Binary files /dev/null and b/sites/www/content/blog/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation/images/1Db6XWyMRM9gGhSEpHY6w7A.webp differ diff --git a/sites/www/content/blog/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation/images/1Pp2MNotBJdSHDcW3Fhz19A.gif b/sites/www/content/blog/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation/images/1Pp2MNotBJdSHDcW3Fhz19A.gif new file mode 100644 index 0000000000..016a223333 Binary files /dev/null and b/sites/www/content/blog/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation/images/1Pp2MNotBJdSHDcW3Fhz19A.gif differ diff --git a/sites/www/content/blog/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation/images/1ZI2zrU2e21dkkHtn49qskw.webp b/sites/www/content/blog/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation/images/1ZI2zrU2e21dkkHtn49qskw.webp new file mode 100644 index 0000000000..26263e1493 Binary files /dev/null and b/sites/www/content/blog/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation/images/1ZI2zrU2e21dkkHtn49qskw.webp differ diff --git a/sites/www/content/blog/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation/images/1iAiugn-WYkk7620I7TgMVg.webp b/sites/www/content/blog/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation/images/1iAiugn-WYkk7620I7TgMVg.webp new file mode 100644 index 0000000000..51023c1b18 Binary files /dev/null and b/sites/www/content/blog/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation/images/1iAiugn-WYkk7620I7TgMVg.webp differ diff --git a/sites/www/content/blog/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation/index.md b/sites/www/content/blog/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation/index.md new file mode 100644 index 0000000000..a506996ea3 --- /dev/null +++ b/sites/www/content/blog/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation/index.md @@ -0,0 +1,149 @@ +--- +title: "Improving perceived performance with image placeholders, precaching, and disabled navigation…" +description: "Perceived performance is how fast an application feels to the user. This article covers three strategies that you can use in your…" +publishDate: 2020-05-26 +author: perclasson +image: images/1Pp2MNotBJdSHDcW3Fhz19A.gif +category: tutorial +layout: blog +--- + +## Improving perceived performance with image placeholders, precaching, and disabled navigation transitions + +Perceived performance is how fast an application feels to the user. This article covers three strategies that you can use in your application to improve perceived performance: image placeholders, precaching images, and disabling navigation transitions. + +## Image placeholders to prevent content from jumping around + +When a user is waiting for images to load, and then they eventually show up, the layout can shift around. By leaving space in the layout for image placeholders, you can avoid this shifting to ensure a better user experience. + +See the following GIF for an example of how it can look without using any placeholders: + +<DashImage figure src="images/1Pp2MNotBJdSHDcW3Fhz19A.gif" alt="[See full interactive example on DartPad.](https://dartpad.dev/embed-flutter.html?gh_owner=perclasson&gh_repo=flutter_code&gh_path=images_jumping_around/lib&theme=dark)" caption="[See full interactive example on DartPad.](https://dartpad.dev/embed-flutter.html?gh_owner=perclasson&gh_repo=flutter_code&gh_path=images_jumping_around/lib&theme=dark)" /> + + +If you already have a placeholder image cached and loaded in your application you can use the [`FadeInImage`](https://api.flutter.dev/flutter/widgets/FadeInImage-class.html) widget to show placeholders. If you want to use a widget instead of an image as a placeholder, you can achieve this with the [`Image.frameBuiler`](https://api.flutter.dev/flutter/widgets/Image/frameBuilder.html) property. + +The `Image.frameBuilder` property is responsible for building the Image widget and it has four arguments: + +1. The build `context`. + +1. The image widget `child`. + +1. A number representing the `frame`, which is null when the image is still loading. + +1. A boolean `wasSynchronouslyLoaded` that is true if the image is already loaded. + +When implementing a placeholder widget, first check whether the image has already been loaded with `wasSynchronouslyLoaded` and, if so, return the `child`. If not, use [`AnimatedSwitcher`](https://api.flutter.dev/flutter/widgets/AnimatedSwitcher-class.html) to create a cross-fade between the placeholder and the image as it loads: + +``` +class ImageWidgetPlaceholder extends StatelessWidget { + const ImageWidgetPlaceholder({ + required this.image, + required this.placeholder, + }) : super(key: key); + + final ImageProvider image; + final Widget placeholder; + +@override + Widget build(BuildContext context) { + return Image( + image: image, + frameBuilder: (BuildContext context, Widget child, int? frame, + bool? wasSynchronouslyLoaded) { + if (wasSynchronouslyLoaded == true) { + return child; + } else { + return AnimatedSwitcher( + duration: const Duration(milliseconds: 500), + child: frame != null ? child : placeholder, + ); + } + }, + ); + } +} +``` + + +After adding placeholders, the layout no longer shifts around, and instead the images fade in as they load: + +<DashImage figure src="images/1ZI2zrU2e21dkkHtn49qskw.webp" alt="[See full interactive example on DartPad.](https://dartpad.dev/embed-flutter.html?gh_owner=perclasson&gh_repo=flutter_code&gh_path=images_with_placeholders/lib&theme=dark)" caption="[See full interactive example on DartPad.](https://dartpad.dev/embed-flutter.html?gh_owner=perclasson&gh_repo=flutter_code&gh_path=images_with_placeholders/lib&theme=dark)" /> + + +## Precaching images before they are displayed + +If your app has a splash or welcome screen before images are shown, you can also precache those images by calling the [`precacheImage`](https://api.flutter.dev/flutter/widgets/precacheImage.html) function. + +``` +precacheImage(NetworkImage(url), context); +``` + + +The following GIF shows an example of precaching images on a Welcome screen: + +<DashImage figure src="images/1iAiugn-WYkk7620I7TgMVg.webp" alt="[See full interactive example on DartPad.](https://dartpad.dev/embed-flutter.html?gh_owner=perclasson&gh_repo=flutter_code&gh_path=precached_images/lib&theme=dark)" caption="[See full interactive example on DartPad.](https://dartpad.dev/embed-flutter.html?gh_owner=perclasson&gh_repo=flutter_code&gh_path=precached_images/lib&theme=dark)" /> + + +## Disabling navigation transitions on Flutter web + +[Navigation transitions](https://material.io/design/navigation/navigation-transitions.html#about-navigation-transitions) occur when a user moves between pages, and it can be a great way to let the user orient themselves in a mobile application. However, for web applications, it’s not something you would typically see. For a perceived performance improvement, you can disable the page transition animation. + +By default, `MaterialApp` uses page transitions for routing relevant to the platform (slide in upwards for Android or from the side for iOS). To override this behavior, you can create your own [`PageTransitionsTheme`](https://api.flutter.dev/flutter/material/PageTransitionsTheme-class.html) class. To detect when the application runs on the web, use the [`kIsWeb`](https://api.flutter.dev/flutter/foundation/kIsWeb-constant.html) constant. If it is on the web, disable the transition by returning the child: + +``` +import 'package:flutter/material.dart'; + +class NoTransitionsOnWeb extends PageTransitionsTheme { + @override + Widget buildTransitions<T>( + route, + context, + animation, + secondaryAnimation, + child, + ) { + if (kIsWeb) { + return child; + } + return super.buildTransitions( + route, + context, + animation, + secondaryAnimation, + child, + ); + } +} +``` + + +Set the `pageTransitionsTheme` for our `MaterialApp`: + +``` +MaterialApp( + theme: ThemeData( + pageTransitionsTheme: NoTransitionsOnWeb(), + ), +) +``` + + +The page transition without any animation: + +<DashImage figure src="images/1Db6XWyMRM9gGhSEpHY6w7A.webp" alt="[See full interactive example on DartPad.](https://dartpad.dev/embed-flutter.html?gh_owner=perclasson&gh_repo=flutter_code&gh_path=precached_images/lib&theme=dark)" caption="[See full interactive example on DartPad.](https://dartpad.dev/embed-flutter.html?gh_owner=perclasson&gh_repo=flutter_code&gh_path=precached_images/lib&theme=dark)" /> + + +## **Conclusion** + +I hope you found some useful tips in this article for how to improve the perceived performance in a Flutter web application. For the [Flutter Gallery](https://gallery.flutter.dev/#/), we disabled the page transitions on the web and added placeholders for images to avoid a layout shift while loading. The implementation is similar to what is described in this article, and if you want to see the code you can find it on [GitHub](https://github.com/flutter/gallery). + +Thank you for reading! + +This post is a part of a series about what we learned when improving performance for the [Flutter Gallery](https://gallery.flutter.dev/#/). Articles in the *Creating performant Flutter web apps* series: + +* [Optimizing performance in Flutter web apps with tree shaking and deferred loading](https://medium.com/flutter/optimizing-performance-in-flutter-web-apps-with-tree-shaking-and-deferred-loading-535fbe3cd674) + +* Improving perceived performance with image placeholders, precaching, and disabled navigation transitions (this article) + +* [Building performant Flutter widgets](https://medium.com/flutter/building-performant-flutter-widgets-3b2558aa08fa) \ No newline at end of file diff --git a/sites/www/content/blog/improving-platform-channel-performance-in-flutter/images/02fUEEmUdocImYO4x.webp b/sites/www/content/blog/improving-platform-channel-performance-in-flutter/images/02fUEEmUdocImYO4x.webp new file mode 100644 index 0000000000..8adc280ad8 Binary files /dev/null and b/sites/www/content/blog/improving-platform-channel-performance-in-flutter/images/02fUEEmUdocImYO4x.webp differ diff --git a/sites/www/content/blog/improving-platform-channel-performance-in-flutter/images/10lyQIh8t-STSy1o2Yjj1tQ.webp b/sites/www/content/blog/improving-platform-channel-performance-in-flutter/images/10lyQIh8t-STSy1o2Yjj1tQ.webp new file mode 100644 index 0000000000..7f54886ede Binary files /dev/null and b/sites/www/content/blog/improving-platform-channel-performance-in-flutter/images/10lyQIh8t-STSy1o2Yjj1tQ.webp differ diff --git a/sites/www/content/blog/improving-platform-channel-performance-in-flutter/index.md b/sites/www/content/blog/improving-platform-channel-performance-in-flutter/index.md new file mode 100644 index 0000000000..58bcd96930 --- /dev/null +++ b/sites/www/content/blog/improving-platform-channel-performance-in-flutter/index.md @@ -0,0 +1,81 @@ +--- +title: "Improving Platform Channel Performance in Flutter" +description: "Over the past couple of years, I’ve been interested in the problem of “How do we make communication between Flutter and its host platform…" +publishDate: 2021-06-14 +author: gaaclarke +image: images/10lyQIh8t-STSy1o2Yjj1tQ.webp +category: announcements +layout: blog +--- + +<DashImage figure src="images/10lyQIh8t-STSy1o2Yjj1tQ.webp" /> + + +Over the past couple of years, I’ve been interested in the problem of “How do we make communication between Flutter and its host platform faster and easier?” This is a question of particular interest to Flutter plugin developers and add-to-app developers. + +Communication between Flutter and the host platform is typically done with [platform channels](https://flutter.dev/docs/development/platform-integration/platform-channels), so my energy has been focused there. In late 2019, to remedy the amount of boilerplate and [stringly typed](https://wiki.c2.com/?StringlyTyped) code required to use platform channels, I designed a `codegen` package, [Pigeon](https://pub.dev/packages/pigeon), that makes platform channels type-safe, and the team continues to improve it. In spring 2020, I performed an [audit of platform channel and foreign function interface (FFI) performance](https://docs.google.com/document/d/1bD_tiN987fWEPtw7tjXHzqZVg_g9H95IS32Cm609VZ8/edit). Now, I’ve set my sights on [improving the performance](https://docs.google.com/document/d/1oNLxJr_ZqjENVhF94-PqxsGPx0qGXx-pRJxXL6LSagc/edit?usp=sharing) of platform channels. Because Pigeon is built on top of platform channels, and I plan to build a [data synchronization solution](http://flutter.dev/go/data-sync) for multiple Flutter instances on top of Pigeon, this is a good opportunity to help with the many different needs of developers and with my initiatives. + +After some investigation, I was able to identify superfluous copies of the data being sent over platform channels and was able to remove them. Below you’ll find the results of that change and an overview of the work that lead to identifying and removing the copies. + +## Results + +After removing the superfluous copies when sending 1 MB of binary data from Flutter to the host platform with a 1 MB response, we saw an approximately [42% increase in performance on iOS](https://flutter-flutter-perf.skia.org/e/?begin=1620764044&end=1621044607&queries=sub_result%3Dplatform_channel_basic_binary_2host_1MB%26test%3Dmac_ios_platform_channels_benchmarks_ios&requestType=0). On Android, the results were a bit more nuanced. Our automated performance tests [improved approximately 15%](https://flutter-flutter-perf.skia.org/e/?begin=1621972627&end=1622677144&queries=sub_result%3Dplatform_channel_basic_binary_2host_1MB%26test%3Dlinux_platform_channels_benchmarks&requestType=0) while local testing saw an approximately [52% increase](https://github.com/flutter/engine/pull/26331#issuecomment-854071096) when migrating to the new [`BinaryCodec.INSTANCE_DIRECT`](https://github.com/flutter/engine/blob/b3ebb6dd62cefe3c30a7bd15ed73c578030140e2/shell/platform/android/io/flutter/plugin/common/BinaryCodec.java#L27) codec. This discrepancy might be because the automated performance tests are running on an older device, but the discrepancy could be an artifact of how the microbenchmarks, in particular, perform on an older device (for example, hammering the garbage collector). You can find the automated performance test’s source code at [platform_channels_benchmarks/lib/main.dart](https://github.com/flutter/flutter/blob/00bfe9061369bb6fdfe4a74fb27086b77df107bf/dev/benchmarks/platform_channels_benchmarks/lib/main.dart#L165). + +For platform channels that use the `StandardMessageCodec`, I saw less of a performance gain (approximately [5% with a 14k payload](https://flutter-flutter-perf.skia.org/e/?begin=1620764044&end=1621044607&queries=sub_result%3Dplatform_channel_basic_standard_2host_large%26test%3Dmac_ios_platform_channels_benchmarks_ios&requestType=0)). I tested it with a large array of supported types to stress test the encoding and decoding. What I found was that the encoding and decoding time of `MessageCodec`s dwarfed the time spent copying the messages between platforms. Most of this encoding time is because of the cost of recursing through a data structure and using reflection to figure out what its contents are. + +So, your mileage might vary depending on how you use platform channels and your device. If you want the fastest communication with platform channels, then you should use `BasicMessageChannel`s with the [`FlutterBinaryCodec`](https://github.com/flutter/engine/blob/b3ebb6dd62cefe3c30a7bd15ed73c578030140e2/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h#L52) on iOS and [`BinaryCodec.INSTANCE_DIRECT`](https://github.com/flutter/engine/blob/b3ebb6dd62cefe3c30a7bd15ed73c578030140e2/shell/platform/android/io/flutter/plugin/common/BinaryCodec.java#L27) on Android, and develop your own protocol for encoding and decoding messages that doesn’t rely on reflection. (Implementing a new `MessageCodec` might be cleaner.) + +If you want to play with the new faster platform channels, they’re now available on the [*master* channel](https://flutter.dev/docs/development/tools/sdk/upgrading#switching-flutter-channels). + +## Copy removal in detail + +If you aren’t interested in a deep dive of how I achieved these results, and the issues I had to overcome, stop reading now. If you enjoy understanding the details, read on. + +The platform channels API hasn’t changed much since 2017. Because the platform channels are fundamental to engine and plugin operation, they aren’t easy to change. While I had a general idea about how platform channels operate, they are somewhat intricate. So, the first step to improving their performance was to understand exactly what they do. + +The following diagram outlines the original process the framework followed when a platform channel was used to communicate with iOS from Flutter: + +<DashImage figure src="images/02fUEEmUdocImYO4x.webp" /> + + +Some takeaways from the diagram: + +* The message hops from the UI thread to the platform thread and back to the UI thread. (In Flutter engine parlance, the UI thread is where Dart is executed, and the platform thread is the main thread of the host platform.) + +* The message and its response use C++ as the intermediate layer for communicating between Flutter and the host platform’s target language. + +* The message’s information was copied 4 times before reaching the Objective-C (Obj-C) handler (steps 3, 5, 7, 8). Steps 3 and 8 perform a translation, while steps 5 and 8 perform a copy, to transfer ownership of the data to a new memory layout. The same process is repeated in reverse for the reply. + +* Steps 1, 9, and 16 are code written by developer who use Flutter. + +Sending a message from Flutter to Java/Kotlin is similar, except there is a Java Native Interface (JNI) layer between C++ and the Java Virtual Machine (JVM). + +After having established how platform channels work, it became clear that eliminating the copies made when transferring data between these layers (such as from C++ to Obj-C) is an obvious method to improve performance. To achieve this, the Flutter engine would have to place the data in memory in a way that is directly accessible from Java/Obj-C and has memory management semantics which are compatible with the host platform. + +The platform channel messages are ultimately consumed by the `decodeMessage` method of the host platform’s `MessageCodec`. On Android, that means a [`ByteBuffer`](https://github.com/flutter/engine/blob/58459a5e342f84c755919f2ad5029b22bcddd548/shell/platform/android/io/flutter/plugin/common/MessageCodec.java#L38), and on iOS, that means [`NSData`](https://github.com/flutter/engine/blob/58459a5e342f84c755919f2ad5029b22bcddd548/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h#L38). The data in C++ needs to conform to those interfaces. When approaching this problem, I discovered that the information of the message resided in C++ memory as a [`std::vector`](https://github.com/flutter/engine/blob/70ebfc3610c38c463469ffedea85578f35ccc0a0/lib/ui/window/platform_message.h#L39) inside a `PlatformMessage` object that was maintained by a [shared pointer](https://en.wikipedia.org/wiki/Smart_pointer). This means that developers couldn’t safely remove the copy when sending the data from C++ to the host platform because they didn’t have a guarantee that the data wouldn’t be mutated by C++ after it was handed over to the host platform. Furthermore, I had to be careful because the `BinaryCodec` implementations treated `encodeMessage` and `decodeMessage` as a no-op, which could lead to code using `BinaryCodec` unwittingly receiving a direct `ByteBuffer`. While it’s unlikely that someone would be surprised by changes to `MessageCodec`, rarely does anyone implement their own codec. Using `BinaryCodec`s, on the other hand, is very common. + +After reading through the code, I discovered that, while the `PlatformMessage` was managed by a shared pointer, it was semantically a unique pointer. The intent was that only one client had access to it at a time (that wasn’t quite the case because momentarily multiple copies existed when passing the `PlatformMessage` between threads, but that was just for convenience and not actually intended). That meant we could migrate from shared pointers to unique pointers, allowing us to pass the data to the host platform safely. + +After [migrating to unique pointers](https://github.com/flutter/engine/commit/7424400f07be684bd87633bbe2d263821181345a#diff-d5a1c9b29bed0d80dc68f228550643925a216e65173364e1ae5a03067b60160d), I had to find a way to pass ownership of the information from C++ to Obj-C. (I implemented Obj-C first, and I’ll discuss Java in more detail later.) The information was stored in an `std::vector` which has no way to release ownership of the underlying buffer. Your only options are to copy out the data, provide an adapter that has the `std::vector`, or eliminate the use of the `std::vector`. + +My first attempt was to subclass `NSData` that would `std::move` the `std::vector` and read its data from there, thus eliminating the copy. This attempt didn’t work well because it turns out that `NSData` is a [class cluster](https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/ClassClusters/ClassClusters.html) in [Foundation](https://developer.apple.com/documentation/foundation?language=objc). That means you can’t just subclass `NSData`. After reading through many of Apple’s documents, it appears that their recommendation is to use composition and message forwarding to make an object behave and look like an `NSData`. That would fool those who use the proxy object, except for those who call `-[NSObject isKindOfClass:]`. While that is unlikely, I couldn’t rule out the possibility. Although I think there might have been some fiddling with the Obj-C runtime that could have made the object behave the way I wanted, it was getting complicated. I instead opted to move the memory out of `std::vector` and into [our own buffer class](https://github.com/flutter/engine/commit/b0bb8eab1d2f7e58230298c28a28ddfeddedeb64#diff-d5a1c9b29bed0d80dc68f228550643925a216e65173364e1ae5a03067b60160d) that allows releasing ownership of the data. That way, I could use `-[NSData dataWithBytesNoCopy:length:]` to transfer ownership of the data to Obj-C. + +Duplicating this process on Android proved a bit more difficult. On Android, platform channels conform to `ByteBuffer` that has the concept of [direct `ByteBuffer`s](https://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html), which allow Java code to interface directly with memory that is laid out in C/C++ style. In a short time, I implemented the move to direct `ByteBuffer`s, but I didn’t see the improvement that I expected. I spent a lot of time learning Android profiling tools, and I eventually opted for trace statements when those failed or returned things I couldn’t believe. It turned out that scheduling the reply to the platform channel message on the UI thread from the platform thread was massively slow, and it seemed to be slow in such a way that the slowdown scaled with the payload of the message. Long story short, I was compiling the Dart VM with the incorrect compilation flags, thinking — no-optimization meant no [link-time optimization](https://en.wikipedia.org/wiki/Interprocedural_optimization), but the flag was actually for runtime optimization. + +In my excitement at having found my blunder, I forgot about the ramifications of using a direct `ByteBuffer` when sending the data into Flutter client code, specifically through custom `MessageCodec`s or clients of `BinaryCodec`. Sending a direct `ByteBuffer` means you have a Java object that is communicating with C/C++ memory, so if you delete the C/C++ memory, then Java interacts with random garbage and will probably crash with an access violation from the operating system. + +Following the example of iOS, I attempted to pass ownership of the C/C++ memory to Java, such that it deletes the C/C++ memory when the Java object is garbage collected. It turns out that doing this isn’t possible when the direct `ByteBuffer` is created from the JNI through [`NewDirectByteBuffer`](https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#NewDirectByteBuffer). JNI provides no hook to know when a Java object is deleted. You can’t subclass `ByteBuffer` so that it calls the JNI when it’s finalized. The only hope would be to allocate the direct `ByteBuffer` from the Java API at step 5 in the preceding diagram. Direct `ByteBuffer`s that are allocated through Java don’t have this limitation. Introducing a new entry-point into Java however would have been a massive change, and anyone who has worked with JNI knows that it’s perilous. + +Instead, I opted to petition the team to accept direct `ByteBuffer`s in `decodeMessage` calls. At first, I introduced a new method to `MessageCodec`*,* `bool wantsDirectByteBufferForDecoding()`, to make sure no one got a direct `ByteBuffer` unless they asked for it and knew the semantics of them (that is, when the underlying C/C++ memory is still valid). That proved to be complicated, and the worry was that developers might still subscribe and not know the semantics of the direct `ByteBuffer`s because they operate contrary to typical `ByteBuffer`s, and might have had their C memory backing deleted underneath them. Storing the encoded buffers was atypical usage on top of unlikely usage, but the team couldn’t rule it out. After many discussions and negotiations, we decided that every `MessageCodec` gets a direct `ByteBuffer` that is cleared out after `decodeMessage` is called. That way, if someone caches encoded messages, then they’ll get a deterministic and apropos error in Java if they try to use the `ByteBuffer` after the underlying C memory is cleaned up. + +Giving everyone access to the performance gains of direct `ByteBuffers` worked great, but was a breaking change to `BinaryCodec` whose `encodeMessage` and `decodeMessage` implementations are no-ops, they just forward their input as their return value. To keep the same memory semantics for `BinaryCodec`, I introduced a [new instance variable](https://github.com/flutter/engine/blob/01d1ed459a313f19e2e01cf8d62331d19b907637/shell/platform/android/io/flutter/plugin/common/BinaryCodec.java#L29) that controls whether the decoded message is a direct `ByteBuffer` (new, faster code) or a standard `ByteBuffer` (old, slower code). We couldn’t create a way to give the performance speed up to all clients of `BinaryCodec`. + +## Future work + +Now that eliminating the copies is done, my next efforts to improve communication between Flutter and the host platform will be: + +1. Implement a custom `MessageCodec` for Pigeon that doesn’t rely on reflection for faster encoding and decoding. + +1. Implement FFI platform channels that allow you to call from Dart to the host platform without hopping between the UI and the platform thread. + +I hope you enjoyed this deep dive into the details of this performance improvement! \ No newline at end of file diff --git a/sites/www/content/blog/index.md b/sites/www/content/blog/index.md new file mode 100644 index 0000000000..97e53018ea --- /dev/null +++ b/sites/www/content/blog/index.md @@ -0,0 +1,8 @@ +--- +title: "The Flutter Blog" +description: "Build beautiful native apps in record time" +layout: blog +showBreadcrumbs: false +--- + +<BlogIndex /> diff --git a/sites/www/content/blog/insights-from-flutters-first-user-survey-of-2019/images/1BvDZf83WrPTc4yqukshP-w.webp b/sites/www/content/blog/insights-from-flutters-first-user-survey-of-2019/images/1BvDZf83WrPTc4yqukshP-w.webp new file mode 100644 index 0000000000..737fe12c17 Binary files /dev/null and b/sites/www/content/blog/insights-from-flutters-first-user-survey-of-2019/images/1BvDZf83WrPTc4yqukshP-w.webp differ diff --git a/sites/www/content/blog/insights-from-flutters-first-user-survey-of-2019/index.md b/sites/www/content/blog/insights-from-flutters-first-user-survey-of-2019/index.md new file mode 100644 index 0000000000..3d32dc9061 --- /dev/null +++ b/sites/www/content/blog/insights-from-flutters-first-user-survey-of-2019/index.md @@ -0,0 +1,81 @@ +--- +title: "Insights from Flutter’s first user survey of 2019" +description: "As we do every quarter, the Flutter UX research team ran a user survey in Q1 to gather your opinions, and we are back to share the results…" +publishDate: 2019-04-12 +author: jayoung-lee +image: images/1BvDZf83WrPTc4yqukshP-w.webp +category: announcements +layout: blog +--- + +As we do every quarter, the Flutter UX research team ran a user survey in Q1 to gather your opinions, and we are back to share the results. In this survey, we heard from 1,961 users. As shown in the graph below, we discovered that 42% of the respondents had used Flutter for less than 3 months. So first of all, welcome all new users to the community! + +<DashImage figure src="images/1BvDZf83WrPTc4yqukshP-w.webp" alt="42% of the respondents had used Flutter for less than 3 months" caption="42% of the respondents had used Flutter for less than 3 months" /> + + +## Are users satisfied with Flutter? + +Very similar to last time, 91% of the respondents were somewhat or very satisfied with Flutter ([92%](https://medium.com/flutter-io/what-weve-learned-from-the-july-2018-flutter-user-survey-cbbf1e04370c) and [93%](https://medium.com/flutter-io/how-we-use-user-surveys-to-prioritize-engineering-effort-in-flutter-october-2018-edition-a26e45abf5aa) in recent surveys). Because the number of Flutter users has tripled over the last three quarters, keeping up the high level of satisfaction has been a surprise and delight to the team. + +Of all respondents, about half (49%) have already invested significant effort into Flutter by publishing an app or testing an app for release. Another half of the respondents (41%) said that they were experimenting with Flutter’s functionality. Whether you are in the former or the latter category, please file an issue on [GitHub](https://github.com/flutter/flutter/issues) with any problems that you encounter, so the team can improve your development experience. + +The purpose of the quarterly survey is to get feedback on what needs improving so that we can continue to please the developers. + +## What can we do better? + +We identified some room for improvement. First, satisfaction with package and plugin ecosystem decreased (74 → 67% satisfied), and 55% of the respondents said that the immature ecosystem is the major blocker of adopting Flutter for main job. Because ecosystem has been one of our main themes to focus in year 2019, we will continue investing in growing the ecosystem around the core Flutter technologies. We plan to improve the quality of Flutter’s first-party plugins, as described in our [published roadmap](https://github.com/flutter/flutter/wiki/Roadmap). If you are interested in contributing to Flutter’s ecosystem, please take a look at the “[Developing packages and plugins](https://flutter.dev/docs/development/packages-and-plugins/developing-packages)” documentation. As a part of the effort to improve the ecosystem, we are [refactoring how Android plugins are compiled](https://github.com/flutter/flutter/issues/29328) to mitigate the [AndroidX](https://flutter.dev/docs/development/packages-and-plugins/androidx-compatibility) incompatibility problem. We are also improving our ability to respond to pull requests with a new pull request [template](https://github.com/flutter/plugins/blob/master/.github/PULL_REQUEST_TEMPLATE.md) and a simple [integration testing](https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md#running-the-tests) method. + +Second, we found that the satisfaction with Cupertino (iOS-style) widgets is still much lower than that of Material Design widgets (66% vs. 88%), though we’ve made a lot of improvements in the past few quarters. Respondents said that it is hard to learn how to use the existing Cupertino widgets (42%), and the widgets are incomplete in their functionality (40%) or not available (38%). This is probably because our official documentation mainly focuses on customizing UIs more than using platform-specific widgets like Cupertino. We will continue our effort on dedicating more website resources to document iOS specific features, supporting more widgets, and improving the fidelity of the widgets. As we address these needs, you can track the improvements in our [changelog](https://github.com/flutter/flutter/wiki/Changelog) and [documentation](https://flutter.dev/docs/development/ui/widgets/cupertino). + +Lastly, we learned that three quarters (75%) of respondents were satisfied with Dart programming language. Similar to Cupertino widgets, unsatisfied users said that Dart lacks learning materials (39%), although it is easy to learn (only 8% finding it difficult). Users also mentioned that Dart lacks language features (37%). We internalize it as a shortfall of documentation and socialization, as well as room for improvement as a language. To respond to these needs, the Dart team is working on a redesigned Dart website with additional documentation, and on evolving the Dart programming language, for example with the recently introduced [Set Literal](https://medium.com/dartlang/announcing-dart-2-2-faster-native-code-support-for-set-literals-7e2ab19cc86d), the upcoming [UI-as-code features](https://medium.com/dartlang/making-dart-a-better-language-for-ui-f1ccaf9f546c), and investigations of a larger set of [potential future features](https://github.com/dart-lang/language/projects/1). + +## Breaking changes for the greater good? + +Breaking changes refer to changes that occur with version upgrades that would involve having to change your previously written code. If a breaking change is introduced in Flutter, the API for new code would be easier to learn and use, but you may not be able to run code written in the past using the up-to-date SDK. + +As described in the [Wiki](https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes), in general, we want to avoid making changes to Flutter, its dependencies, our plugins, or our packages, that force developers using Flutter to change their code in order to upgrade to new versions of Flutter. Sometimes, however, breaking changes are necessary for the greater benefit to the community, for example, when better features are developed or function issues emerge. + +Hence, we were interested in learning your opinions on breaking changes. In the survey, 30% of the respondents who had upgraded Flutter in the past experienced breaking changes. 32% of them said that it was frustrating that they had to keep updating the code. Yet, more people — 47% — thought that the changes made the code cleaner, and 49% thought that the changes were valuable to them. Most surprising of all, however, 70% of the people who said they were frustrated with updating their code due to a breaking change said that they would nonetheless like us to make necessary breaking changes to improve our APIs. (Overall, 78% said that they would like us to make breaking changes to have the cleanest possible API that is easy to learn and use.) + +From this result, we could learn that Flutter’s current user base has more positive than negative attitude towards breaking changes. + +## How do users want Flutter’s API to evolve? + +We presented three specific scenarios to gauge the preference between unmodified APIs versus clean APIs. The first scenario was as follows:. + +*“Imagine that you are in the year 2020. You find a seemingly relevant Flutter sample online, which dates back to 2018. But there are compiler errors because the Flutter API has evolved over the last two years. What would you think?”* + +To this question, 86% said that such broken sample is acceptable, and they were willing to deal with some breaking changes as a trade-off for a cleaner API. + +Similarly, we posed another scenario where Flutter for desktop introduces breaking changes. The question was: + +*“When we add support for building desktop applications to Flutter, which of the following would be preferable for you?”* + +To this question, 75% selected *“For the API to be clean, even though this would require significant changes to your mobile apps as well”* over *“For existing apps to continue working unmodified in future Flutter SDKs that provide desktop support, even though this might mean that the desktop API features for desktop features are hard to use.”* Remarkably, even half of those who are very *unlikely* to build a desktop app with Flutter (47%) picked the former option as well. + +In addition, more than half of the users expressed interest in some specific breaking changes, such as: a modern string API, changing widget names to make them more consistent (e.g., renaming SliverMultiBoxAdapt**o**rWidget to SliverMultiBoxAdapt***e***rWidget to be more consistent with SliverToBoxAdapt***e***r), and non-nullable-by-default types. + +We also carefully read all the comments and learned that although it could be frustrating, respondents were generally less worried about updating code, as the users can pin their target client version and change slowly. We learned that breaking changes could be less disruptive if we: + +* change slowly, e.g. start from beta channel, introduce it as a library first + +* offer a good migration tool + +* provide documentation, e.g., examples, tutorials, version change logs + +* clearly explain the benefit + +* broadly announce it + +Many comments suggested to change early, with some careful steps. Some quotes from the survey are below. +> “Breaking change should be done as soon as possible, while Flutter is yet getting its popularity.” +> “I don’t mind having breaking changes it there is information provided by the debugger or an automated tool to specify what has changed and specifying how it should be now.” +> “I wish you change slowly, fully documented, and broadly announced” + +The survey results helped the team understand preferences and concerns around breaking changes. However, the fact that many people favored clean API does not mean we could take breaking change decisions lightly. We hear that respondents also had deep concerns about breaking package and dependencies. Before each change, we will carefully assess its benefit, solicit feedback from the community, and make appropriate decisions. In the near future, we will publish a formal breaking change policy on the GitHub Wiki that evolves [our current policy](https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes) with the lessons we’ve taken from this feedback. + +## And finally… + +Thank you again everyone for participating in the survey. Please look out for the next survey, which will be announced on the flutter.dev website and community channels like Twitter ([@flutterdev](https://twitter.com/flutterdev)) and Reddit ([r/FlutterDev](https://www.reddit.com/r/FlutterDev/)). + +Flutter’s UX research team is also performing a wide variety of user experience studies to make your experience with Flutter more pleasant. If you are interested in participating, you can [sign up](http://flutter.dev/research-signup) for future studies. \ No newline at end of file diff --git a/sites/www/content/blog/integration-testing-with-flutter-driver/images/0-1m66Jxnec3s8Qnb.webp b/sites/www/content/blog/integration-testing-with-flutter-driver/images/0-1m66Jxnec3s8Qnb.webp new file mode 100644 index 0000000000..e2a37ac271 Binary files /dev/null and b/sites/www/content/blog/integration-testing-with-flutter-driver/images/0-1m66Jxnec3s8Qnb.webp differ diff --git a/sites/www/content/blog/integration-testing-with-flutter-driver/index.md b/sites/www/content/blog/integration-testing-with-flutter-driver/index.md new file mode 100644 index 0000000000..ad6e0e8c4b --- /dev/null +++ b/sites/www/content/blog/integration-testing-with-flutter-driver/index.md @@ -0,0 +1,137 @@ +--- +title: "Integration Testing with flutter_driver" +description: "Unit tests ensure that individual parts of your application work as intended, but what about your application as a whole? This is where…" +publishDate: 2020-09-29 +author: guidez +category: spotlight +layout: blog +--- + +## Integration testing with flutter_driver + +Unit tests ensure that individual parts of your application work as intended, but what about your application as a whole? This is where integration testing comes in. + +## Flutter driver + +If you’ve never done integration testing in Flutter (or anywhere), fear not! Adding integration tests to your app is a straightforward task in Flutter. A very helpful set of articles will guide you. + +1. [An introduction to integration testing](https://flutter.dev/docs/cookbook/testing/integration/introduction): What even is this thing, and how do I set it up? + +1. [Handle scrolling](https://flutter.dev/docs/cookbook/testing/integration/scrolling): How do I handle the common pattern of scrolling to something? + +Getting to this point ensures that your app can run without crashing, that certain screens can be reached, that specific actions have some result, and so on. What tests you create depends on your needs. You can have one test that opens every screen of your app, or tests that go through specific user journeys. + +But we can go further with `flutter_driver` by using it for performance testing. + +## Performance profiling + +Simply wrapping your test with `flutterDriver.traceAction` records the performance of your app as your test is running. This output data (in JSON format) can be used in Continuous Integration (CI) testing, to ensure, for example, that certain metrics remain above or below a particular threshold. The data can also be used to debug performance issues. For details on how to do performance profiling, see the article on integration testing, [Performance profiling](https://flutter.dev/docs/cookbook/testing/integration/profiling). + +For reference, the Flutter Gallery has an [integration test](https://github.com/flutter/gallery/tree/master/test_driver) that goes through all demos and captures performance metrics for a subset of them. + +## Screenshot testing + +*Screenshot testing* is the technique of rendering the UI, capturing a screenshot of the output, and then comparing the result to an expected image. An easy way to add screenshot tests to your app is to use `flutterDriver.screenshot`. To learn more and see a real code example, see the Medium article, [Testing Flutter UI with Flutter Driver](https://medium.com/flutter-community/testing-flutter-ui-with-flutter-driver-c1583681e337#8561), by community member Darshan Kawar. + +This method can be easily integrated into your continuous integration testing setup to prevent UI regressions. The Flutter Gallery has a few [screenshots tests](https://github.com/flutter/gallery/tree/master/golden_test) and a [GitHub workflow configuration](https://github.com/flutter/gallery/blob/master/.github/workflows/golden.yml) to automatically test incoming PRs. + +```dart +await pumpWidgetWithImages( + tester, + const GalleryApp(initialRoute: demoBannerRoute), + homeAssets, +); + +await tester.pumpAndSettle(); + +await expectLater( + find.byType(GalleryApp), + matchesGoldenFile('goldens/demo_desktop_dark.png'), +); + +``` + +## a11y testing + +*a11y*, or accessibility **testing, is a type of usability testing performed to ensure that the application is usable by people with disabilities, such as vision impairment, hearing loss, a motor disability, and so on. Use `flutterDriver.getSemanticsId` to verify semantic labels, for example, to verify that all images have semantic labels. + +```dart +final imageLabel = find.bySemanticsLabel('Company logo'); +int id = await flutterDriver.getSemanticsId(imageLabel); +expect(id, isNotNull); +``` + +Learn more about testing for accessibility in another great article by Darshan Kawar, [Developing and testing accessible apps in Flutter](https://medium.com/flutter-community/developing-and-testing-accessible-app-in-flutter-1dc1d33c7eea). + +## i18n testing + +*i18n*, or internationalization testing, is the process of testing that an application can be used in various languages and regions without any changes. + +Depending on how your localization code is set up, you might change locales using your localizations’ delegate. + +``` +ExampleAppLocalizationsDelegate.load(Locale(‘fr’)); +``` + + +Alternatively, when using MaterialApp, simply allow overriding the locale for the app to run in a different locale. + +```dart +void main() { + runApp(const ExampleApp()); +} + +class ExampleApp extends StatelessWidget { + const ExampleApp({ + Key key, + this.locale, + }) : super(key: key); + + final Locale locale; + + @override + Widget build(BuildContext context) { + return MaterialApp( + locale: locale, + ... + ); + } +} + +``` + +In the driver file that launches the app, set the locale. + +```dart +void main() { + runApp(const ExampleApp(locale: Locale('fr'))); +} + +``` + +## Extra tip + +Want to know if an element is present on the page? + +```dart +/// Returns a [Future] that resolves to true if the widget specified by [finder] +/// is present, false otherwise. +Future<bool> isPresent(SerializableFinder finder, FlutterDriver driver, + {Duration timeout = const Duration(seconds: 1)}) async { + try { + await flutterDriver.waitFor(finder, timeout: timeout); + return true; + } catch (exception) { + return false; + } +} + +``` + +You can choose whatever timeout works for your app. + +## Closing remarks + +By now you should have a good idea of what is possible with `flutter_driver`. You can combine methods to fit your needs; for example, performing screenshot tests using different locales. If we forgot something, let us know in the comments! For more information, see the [flutter_driver](https://api.flutter.dev/flutter/flutter_driver/flutter_driver-library.html) API docs. + +*About the author: Pierre-Louis is a recent undergrad for the University of Waterloo. Based in Munich, he is part of a team that maintains the Flutter Material library. He has a keen interest in UI, UX, and app development. You can connect with him on [LinkedIn](https://www.linkedin.com/in/guidezpl) and [GitHub](https://github.com/guidezpl).* \ No newline at end of file diff --git a/sites/www/content/blog/introducing-background-isolate-channels/images/0Db7Niu7FqKGszecZ.webp b/sites/www/content/blog/introducing-background-isolate-channels/images/0Db7Niu7FqKGszecZ.webp new file mode 100644 index 0000000000..d750b09c16 Binary files /dev/null and b/sites/www/content/blog/introducing-background-isolate-channels/images/0Db7Niu7FqKGszecZ.webp differ diff --git a/sites/www/content/blog/introducing-background-isolate-channels/images/1bSKidxVM7-brUJFuyKdVFg.webp b/sites/www/content/blog/introducing-background-isolate-channels/images/1bSKidxVM7-brUJFuyKdVFg.webp new file mode 100644 index 0000000000..2f4db8b473 Binary files /dev/null and b/sites/www/content/blog/introducing-background-isolate-channels/images/1bSKidxVM7-brUJFuyKdVFg.webp differ diff --git a/sites/www/content/blog/introducing-background-isolate-channels/index.md b/sites/www/content/blog/introducing-background-isolate-channels/index.md new file mode 100644 index 0000000000..9f0d1dc147 --- /dev/null +++ b/sites/www/content/blog/introducing-background-isolate-channels/index.md @@ -0,0 +1,72 @@ +--- +title: "Introducing background isolate channels" +description: "Now as of Flutter 3.7, I’m pleased to announce that developers can use Plugins and Platform Channels from any isolate. This has been one of…" +publishDate: 2023-01-09 +author: gaaclarke +image: images/1bSKidxVM7-brUJFuyKdVFg.webp +category: announcements +layout: blog +--- + +<DashImage figure src="images/1bSKidxVM7-brUJFuyKdVFg.webp" /> + + +Now as of Flutter 3.7, I’m pleased to announce that developers can use Plugins and Platform Channels from *any* isolate. This has been one of our [highest ranked issues](https://github.com/flutter/flutter/issues/13937) and has been around since 2018. It was deprioritized since it wasn’t trivial to implement and there was a workaround, albeit cumbersome: always use Plugins from the root isolate (the isolate that Flutter provides). However, as Flutter has matured it has increasingly focused on performance, following the old software adage “Make it work, make it right, make it fast.” Choosing to implement this feature was a happy intersection of improving performance and also making Flutter easier to use. So, it became an easy decision to make the investment. + +If you want to see how this feature is used, check out the [sample code](https://github.com/flutter/samples/tree/main/background_isolate_channels) on GitHub. + +## Use cases + +Why would someone want to use plugins from a background isolate? Well it’s clear that there is a need for plugins since not all of the world’s code is written in Dart. There are years of effort from the community to make that code accessible using plugins, things like: *path_provider*’s ability to find the temporary directory or *flutter_local_notifications*’s ability to post notifications. + +The next logical question is: “why would someone execute code on a background isolate?” The answer is that sometimes you don’t have a choice, a library might be invoking callbacks on a background isolate, like *android_alarm_manager_plus*. Or an app might be doing significant computations and the developer doesn’t want those to interfere with the UI. + +In my time on the team helping other teams at Google adopt Flutter, it was inevitable that, as a product matured, they would eventually get into a situation where the root isolate would become a bottleneck. So, we need to make sure everything in the framework is optimized and give tools to users to easily offload work when necessary. + +Here is a contrived concrete use case for Background Isolate Channels: + +Imagine an app for generating high resolution images from text prompts with AI. The user’s previous creations are stored on Firebase Cloud Storage and there is a feature to export and share the creations from the user’s phone. The Flutter app launches a background isolate which downloads the 8k version of their image from Firebase Cloud Store, downsample the image to the desired export size, save the image to the camera roll, and finally post a local notification when the export is finished. + +At least 3 plugins were used from a background isolate in this example, one to read from Firebase Cloud Store, one to save to the phone’s camera roll, and one to post a local notification. Without Background Isolate Channels, the app would have to copy the 8k image from the root isolate to the background isolate in order for it to be downsampled. There is no way to make that a constant time operation with Dart today. + +## Quick sample + +Here is a quick sample using the new API to call the *shared_preferences* plugin from a background isolate: + +``` +import ‘package:flutter/services.dart’; +import ‘package:shared_preferences/shared_preferences.dart’; + +void main() { + // Identify the root isolate to pass to the background isolate. + // (API introduced in Flutter 3.7) + RootIsolateToken rootIsolateToken = RootIsolateToken.instance!; + Isolate.spawn(_isolateMain, rootIsolateToken); +} + +void _isolateMain(RootIsolateToken rootIsolateToken) async { + // Register the background isolate with the root isolate. + BackgroundIsolateBinaryMessenger + .ensureInitialized(rootIsolateToken); + // You can now use the shared_preferences plugin. + SharedPreferences sharedPreferences = + await SharedPreferences.getInstance(); + print(sharedPreferences.getBool(‘isDebug’)); +} +``` + + +## Technical details + +Here is a high level overview of how Platform Channels work: + +<DashImage figure src="images/0Db7Niu7FqKGszecZ.webp" /> + + +When the result from the Platform Channel was invoked there was a hardcoded hop to the *platform* thread. In order for Background Isolate Channels to work, the isolate sending the message must be stored so that the engine can schedule the result on that isolate’s event loop. That was implemented by using [Dart’s *ports](https://github.com/dart-lang/sdk/blob/eb9554d70e386bb3177f63509ba8f7e4bbf500a0/runtime/include/dart_native_api.h#L125).* Dart *ports* store the isolate that owns them and is the only way to schedule on those isolates from the C API. + +The other thing needing to be implemented was some way of associating background isolates with their root isolate. This was a surprise to me, but in order to shut down platform channels when a Flutter engine is destructed, one must know what background isolates are associated with that engine. Otherwise a background isolate could try to communicate with a Flutter engine that is in the process of being destroyed. The ramifications of this can be seen in the final API where a *RootIsolateToken* must be used to initialize the *BackgroundIsolateBinaryMessenger*. + +For more information on the implementation, check out the [Isolate Platform Channels](https://docs.google.com/document/d/1yAFw-6kBefuurXWTur9jdEUAckWiWJVukP1Iay8ehyU/edit#heading=h.722pnbmlqbkx) design doc. This doc also contains proposals for communicating in the opposite direction, which have not been implemented or accepted yet. + +Thank you to the Flutter community for your support and I hope you all find amazing uses for this new feature. \ No newline at end of file diff --git a/sites/www/content/blog/introducing-flutter-3/images/087xQ1AYdEF2YrmQ1.webp b/sites/www/content/blog/introducing-flutter-3/images/087xQ1AYdEF2YrmQ1.webp new file mode 100644 index 0000000000..bddd96e182 Binary files /dev/null and b/sites/www/content/blog/introducing-flutter-3/images/087xQ1AYdEF2YrmQ1.webp differ diff --git a/sites/www/content/blog/introducing-flutter-3/images/0LM_w2DE9aM-_9J0Z.webp b/sites/www/content/blog/introducing-flutter-3/images/0LM_w2DE9aM-_9J0Z.webp new file mode 100644 index 0000000000..d03b987bef Binary files /dev/null and b/sites/www/content/blog/introducing-flutter-3/images/0LM_w2DE9aM-_9J0Z.webp differ diff --git a/sites/www/content/blog/introducing-flutter-3/images/0ZQ9Xa7CINFVMA95w.webp b/sites/www/content/blog/introducing-flutter-3/images/0ZQ9Xa7CINFVMA95w.webp new file mode 100644 index 0000000000..63b503ef18 Binary files /dev/null and b/sites/www/content/blog/introducing-flutter-3/images/0ZQ9Xa7CINFVMA95w.webp differ diff --git a/sites/www/content/blog/introducing-flutter-3/images/0pL2z2iYzWPrMu5hw.webp b/sites/www/content/blog/introducing-flutter-3/images/0pL2z2iYzWPrMu5hw.webp new file mode 100644 index 0000000000..08fa5b7fd7 Binary files /dev/null and b/sites/www/content/blog/introducing-flutter-3/images/0pL2z2iYzWPrMu5hw.webp differ diff --git a/sites/www/content/blog/introducing-flutter-3/images/0wK4YI3N-Hh2vtDQ2.webp b/sites/www/content/blog/introducing-flutter-3/images/0wK4YI3N-Hh2vtDQ2.webp new file mode 100644 index 0000000000..7b951257bb Binary files /dev/null and b/sites/www/content/blog/introducing-flutter-3/images/0wK4YI3N-Hh2vtDQ2.webp differ diff --git a/sites/www/content/blog/introducing-flutter-3/images/1mt5JTb-02GrFFtCnW20Tqg.webp b/sites/www/content/blog/introducing-flutter-3/images/1mt5JTb-02GrFFtCnW20Tqg.webp new file mode 100644 index 0000000000..79b54e73c5 Binary files /dev/null and b/sites/www/content/blog/introducing-flutter-3/images/1mt5JTb-02GrFFtCnW20Tqg.webp differ diff --git a/sites/www/content/blog/introducing-flutter-3/index.md b/sites/www/content/blog/introducing-flutter-3/index.md new file mode 100644 index 0000000000..eb8bdd7b1f --- /dev/null +++ b/sites/www/content/blog/introducing-flutter-3/index.md @@ -0,0 +1,101 @@ +--- +title: "Introducing Flutter 3" +description: "The culmination of our journey to multiplatform UI development on phone, desktop, and web" +publishDate: 2022-05-11 +author: timsneath +image: images/0ZQ9Xa7CINFVMA95w.webp +category: releases +layout: blog +--- + +*We’re delighted to announce the launch of Flutter 3 as part of the Google I/O keynote. Flutter 3 completes our roadmap from a mobile-centric to a multiplatform framework, with the availability of macOS and Linux desktop app support, along with improvements to Firebase integration, new productivity and performance features, and support for Apple Silicon.* + +<DashImage figure src="images/0ZQ9Xa7CINFVMA95w.webp" /> + + +## The journey to Flutter 3 + +We started Flutter as an attempt to revolutionize app development: combining the iterative development model of the web with hardware-accelerated graphics rendering and pixel-level control that were previously the preserve of games. Over the last four years since Flutter 1.0 beta, we’ve gradually built on these foundations, adding new framework capabilities and new widgets, deeper integration with the underlying platforms, a rich library of packages and many performance and tooling improvements. + +<DashImage figure src="images/0pL2z2iYzWPrMu5hw.webp" /> + + +As the product has matured, more of you have started to build apps with it. Today there are over 500,000 apps built with Flutter. Analytics from research firms like [data.ai](https://www.data.ai/en/), along with public testimonials, show Flutter is used by a [broad list of customers](https://flutter.dev/showcase) in many segments: from social apps like [WeChat](https://play.google.com/store/apps/details?id=com.tencent.mm&hl=en_US&gl=US) to finance and banking apps like [Betterment](https://apps.apple.com/us/app/betterment-investing-saving/id393156562) and [Nubank](https://play.google.com/store/apps/details?id=com.nu.production&hl=en_US&gl=US); from commerce apps like [SHEIN](https://play.google.com/store/apps/details?id=com.zzkko&hl=en_US&gl=US) and [trip.com](https://apps.apple.com/us/app/trip-com-hotels-flights-trains/id681752345) to lifestyle apps like [Fastic](https://fastic.com/) and [Tabcorp](https://auspreneur.com.au/tabcorp-adopts-googles-flutter-platform/); from companion apps like [My BMW](https://www.press.bmwgroup.com/global/article/detail/T0328610EN/the-my-bmw-app:-new-features-and-tech-insights-for-march-2021?language=en) to public institutions such as the [Brazil government](https://apps.apple.com/app/id1506827551). +> # Today there are over 500,000 apps built with Flutter. + +<YoutubeEmbed id="8RmsstcNE1Y" title="ByteDance - Building for any Screen with Flutter (Flutter Dev Story)" fullwidth="true"/> + + +Developers tell us that Flutter helps build beautiful apps more quickly for more platforms. In our most recent user study: + +* 91% of developers agreed that Flutter reduced the time it took to build and publish an app. + +* 85% of developers agreed that Flutter made their app more beautiful than before. + +* 85% agreed that it enabled them to publish their app for more platforms than before. + +In a [recent blog post by Sonos](https://tech-blog.sonos.com/posts/renovating-setup-with-flutter/) discussing their revamped setup experience, they emphasized the second of these: +> “It is not an exaggeration to say that [Flutter] has unlocked a degree of “*premium*” unlike anything our team had delivered before. Most important to our designers, the ease with which new UIs can be built means that our team spends less time saying “no” to specs and more time iterating on them. If this sounds worth it, we’d recommend giving Flutter a try — we’re glad we did.” + +## Introducing Flutter 3 + +Today, we’re introducing Flutter 3**,** which is the culmination of our journey to fill out the platforms supported by Flutter. With Flutter 3, you can build beautiful experiences for six platforms from a single codebase, giving developers unparalleled productivity and enabling startups to bring new ideas to the full addressable market from day one. + +In previous releases, we supplemented iOS and Android with [web](https://medium.com/flutter/flutter-web-support-hits-the-stable-milestone-d6b84e83b425) and [Windows support](https://medium.com/flutter/announcing-flutter-for-windows-6979d0d01fed), and now **Flutter 3 adds stable support for macOS and Linux apps**. Adding platform support requires more than rendering pixels: it includes new input and interaction models, compilation and build support, accessibility and internationalization, and platform-specific integration. Our goal is to give you the flexibility to take full advantage of the underlying operating system while sharing as much UI and logic as you choose. + +On macOS, we’ve invested in supporting both Intel and Apple Silicon, with [Universal Binary](https://developer.apple.com/documentation/apple-silicon/building-a-universal-macos-binary) support that allows apps to package executables that run natively on both architectures. On Linux, Canonical and Google have collaborated to offer a highly-integrated, best-of-breed option for development. + +A great example of how Flutter enables beautiful desktop experiences is [Superlist](https://superlist.com/), which launches in beta today. Superlist provides supercharged collaboration, through a new app that combines lists, tasks, and freeform content into a fresh take on to-do lists and personal planning. The Superlist team selected Flutter for its ability to deliver a fast, highly-branded desktop experience, and we think their progress to date demonstrates why it has proven to be a great choice. + +<YoutubeEmbed id="YRuQj7mlH2I" title="Superlist Google I/O - Flutter & Superlist" fullwidth="true"/> + + +Flutter 3 also improves on many of the fundamentals, with improved performance, Material You support, and productivity updates. + +In addition to the work mentioned above, with this version, Flutter is fully native on [Apple silicon](https://support.apple.com/en-us/HT211814) for *development*. While Flutter has been compatible with M1-powered Apple devices since their release, Flutter now takes full advantage of [Dart’s support for Apple silicon](https://medium.com/dartlang/announcing-dart-2-14-b48b9bb2fb67), enabling much faster compilation on M1-powered devices and support for [universal binaries](https://developer.apple.com/documentation/apple-silicon/building-a-universal-macos-binary) ****for macOS apps. + +Our work for [Material Design 3](https://m3.material.io/) is largely complete in this release, allowing developers to take advantage of an adaptable, cross-platform design system that offers dynamic color schemes and updated visual components: + +<DashImage figure src="images/0LM_w2DE9aM-_9J0Z.webp" /> + + +Our detailed technical blog post expands on these and the many other new features in Flutter 3. + +Flutter is powered by Dart, a high-productivity, portable language for multiplatform development. Our work on Dart in this cycle includes new language features that reduce boilerplate and aid readability, experimental RISC-V support, an upgraded linter, and new documentation. For further details on all the new improvements in Dart 2.17, check out the [dedicated blog](https://medium.com/dartlang). + +## Firebase and Flutter + +Of course, there’s more to building an app than a UI framework. App publishers need a comprehensive suite of tools to help you build, release, and operate your apps, including services like authentication, data storage, cloud functions, and device testing. There are a variety of services that support Flutter, including [Sentry](https://docs.sentry.io/platforms/flutter/), [AppWrite](https://appwrite.io/docs/getting-started-for-flutter), and [AWS Amplify](https://docs.amplify.aws/start/q/integration/flutter/). + +Google’s offering for app services is Firebase, and [developer benchmarking studies by SlashData](https://www.slashdata.co/developer-program-benchmarking/?) show that 62% of Flutter developers use Firebase in their apps. So over the last few releases, we’ve been working with Firebase to expand and better integrate Flutter as a first-class integration. That has included bringing the Firebase plugins for Flutter to 1.0, adding better documentation and tooling, and new widgets like [FlutterFire UI](https://pub.dev/packages/flutterfire_ui) that provide developers with reusable UI for auth and profile screens. + +Today we’re announcing the graduation of Flutter/Firebase integration to a fully-supported core part of the Firebase offering. We’re moving the source code and documentation into the main Firebase repo and site, and you can count on us evolving Firebase support for Flutter in lockstep with Android and iOS. + +In addition, we’ve made major improvements to support Flutter apps using Crashlytics, Firebase’s popular real-time crash reporting service. With the updates to the [Flutter Crashlytics plugin](https://firebase.google.com/docs/crashlytics), you can track fatal errors in real time, giving you the same set of features that are available to other iOS and Android developers. This includes important alerting and metrics like “crash-free users” that help you stay on top of your app’s stability. The Crashlytics analysis pipeline has been upgraded to improve clustering of Flutter crashes, making it faster to triage, prioritize, and fix issues. And lastly, we streamlined the plugin setup process so that it only requires a couple of steps to get up and running with Crashlytics, right from your Dart code. + +## Flutter Casual Games Toolkit + +For most developers, Flutter is an app framework. But there’s also a growing community around casual game development, taking advantage of the hardware-accelerated graphics support provided by Flutter along with open source game engines like [Flame](https://flame-engine.org/). We want to make it easier for casual game developers to get started, so at I/O today we’re announcing the [Casual Games Toolkit](https://flutter.dev/games), which provides a starter kit of templates and best practices along with credits for ads and cloud services. + +<DashImage figure src="images/0wK4YI3N-Hh2vtDQ2.webp" /> + + +Although Flutter isn’t designed for high-intensity 3D action gaming, even some of those games have turned to Flutter for non-game UI, including popular games like [PUBG Mobile](https://play.google.com/store/apps/details?id=com.tencent.ig) that have hundreds of millions of users. And for I/O we thought we’d see how far we could push our technology, so we’ve created a fun pinball game that is powered by Firebase and Flutter’s web support. I/O Pinball provides a custom table designed around four of Google’s favorite mascots: Flutter’s Dash, Firebase’s Sparky, the Android robot, and the Chrome dinosaur, and lets you compete with others for the high score. We think it’s a fun way to showcase Flutter’s versatility. + +<DashImage figure src="images/087xQ1AYdEF2YrmQ1.webp" /> + + +## Sponsored by Google, powered by community + +One thing we love about Flutter is that it’s not just a Google product — it’s an “everybody” product. Open source means that we can all participate and have a stake in its success, whether by contributing new code or documentation, creating packages that give the core framework new superpowers, writing books and training courses that teach others, or helping organize events and user groups. + +To showcase the community at its best, we recently sponsored a Puzzle Hack challenge in collaboration with DevPost, offering developers the chance to showcase their skills by reimagining the classic sliding puzzle with Flutter. This proved a fantastic demonstration of how web, desktop, and mobile come together: now we can all play the games online or via the stores. + +We put together this video to showcase some of our favorite submissions and prize winners; we think you’ll enjoy it: + +<YoutubeEmbed id="l6hw4o6_Wcs" title="Puzzle App Hackathon winners and more!" fullwidth="true"/> + + +Thank you for your support of Flutter, and welcome to Flutter 3! + +<DashImage figure src="images/1mt5JTb-02GrFFtCnW20Tqg.webp" /> diff --git a/sites/www/content/blog/introducing-google-fonts-for-flutter-v-1-0-0/images/06ZIbMT1ySdIZoHK4.gif b/sites/www/content/blog/introducing-google-fonts-for-flutter-v-1-0-0/images/06ZIbMT1ySdIZoHK4.gif new file mode 100644 index 0000000000..6a0d89df0f Binary files /dev/null and b/sites/www/content/blog/introducing-google-fonts-for-flutter-v-1-0-0/images/06ZIbMT1ySdIZoHK4.gif differ diff --git a/sites/www/content/blog/introducing-google-fonts-for-flutter-v-1-0-0/images/1ndZ8HED7jp2f0X4IFSwtdA.webp b/sites/www/content/blog/introducing-google-fonts-for-flutter-v-1-0-0/images/1ndZ8HED7jp2f0X4IFSwtdA.webp new file mode 100644 index 0000000000..f86de75956 Binary files /dev/null and b/sites/www/content/blog/introducing-google-fonts-for-flutter-v-1-0-0/images/1ndZ8HED7jp2f0X4IFSwtdA.webp differ diff --git a/sites/www/content/blog/introducing-google-fonts-for-flutter-v-1-0-0/index.md b/sites/www/content/blog/introducing-google-fonts-for-flutter-v-1-0-0/index.md new file mode 100644 index 0000000000..c39e4188b7 --- /dev/null +++ b/sites/www/content/blog/introducing-google-fonts-for-flutter-v-1-0-0/index.md @@ -0,0 +1,41 @@ +--- +title: "Introducing Google Fonts for Flutter v 1.0.0!" +description: "By MH Johnson and Anthony Robledo" +publishDate: 2020-04-23 +author: clocksmith +image: images/1ndZ8HED7jp2f0X4IFSwtdA.webp +category: announcements +layout: blog +--- + +By [Anthony Robledo](https://medium.com/@clocksmith) and [MH Johnson](https://medium.com/@johnsonmh) + +<DashImage figure src="images/1ndZ8HED7jp2f0X4IFSwtdA.webp" /> + + +Google Fonts for Flutter was first announced at Flutter Interact in December 2019. With help from the community (thank you!), we have added many new features and improved stability. After a successful time in beta, it is now ready for prime time use on Android, iOS, Web, and MacOS, as [Google Fonts 1.0.0](https://pub.dev/packages/google_fonts). + +Features added since beta: + +* Get a list of all supported fonts with `GoogleFonts.asMap()` + +* Use a font with a dynamic name: `GoogleFonts.getFont(‘Lato’)` + +* Ensure secure downloads with checksum verification + +* Experience improved performance, especially on web + +* Pre-bundle fonts using pubspec assets + +* Optionally disable fetching fonts at runtime (useful when debugging or pre-bundling) + +<DashImage figure src="images/06ZIbMT1ySdIZoHK4.gif" /> + + +Google Fonts allows developers to easily experiment with and use any of the fonts from fonts.google.com in their app. When the app is ready to publish, the developer then decides whether the user receives the font by downloading it from the API, or it’s pre-bundled with the app package. + +We have minimized the work needed to pre-bundle a font, and also kept it compatible with dynamic font loading so you don’t have to change your code if you decide to pre-bundle. + +Check out the [Getting Started Guide](https://pub.dev/packages/google_fonts) to learn how to include fonts in your app via `TextStyle`s or `TextTheme`s. And once you are ready to publish, learn how to optionally pre-bundle fonts and add the font licenses to your app’s `LicenseRegistry`. + +We hope you find this package useful for all of your Google Fonts needs. As always, please never hesitate to leave feedback, [file an issue](https://github.com/material-foundation/google-fonts-flutter/issues), or open a pull request! \ No newline at end of file diff --git a/sites/www/content/blog/introducing-skills-for-dart-and-flutter/images/1-cp6K90CIG7efKBLyiZ_jw.webp b/sites/www/content/blog/introducing-skills-for-dart-and-flutter/images/1-cp6K90CIG7efKBLyiZ_jw.webp new file mode 100644 index 0000000000..cf1e85fcff Binary files /dev/null and b/sites/www/content/blog/introducing-skills-for-dart-and-flutter/images/1-cp6K90CIG7efKBLyiZ_jw.webp differ diff --git a/sites/www/content/blog/introducing-skills-for-dart-and-flutter/index.md b/sites/www/content/blog/introducing-skills-for-dart-and-flutter/index.md new file mode 100644 index 0000000000..aa258bd275 --- /dev/null +++ b/sites/www/content/blog/introducing-skills-for-dart-and-flutter/index.md @@ -0,0 +1,102 @@ +--- +title: "Introducing Skills for Dart and Flutter" +description: "Improving AI with domain expertise" +publishDate: 2026-05-06 +author: mariam_hasnany +image: images/1-cp6K90CIG7efKBLyiZ_jw.webp +layout: blog +--- + +<DashImage figure src="images/1-cp6K90CIG7efKBLyiZ_jw.webp" alt="Introducing prepackaged Dart and Flutter Skills!" caption="Introducing prepackaged Dart and Flutter Skills!" /> + + +## Introducing Skills for Dart and Flutter + +### Improving AI with domain expertise + +AI agents are generalists, but when it comes to professional Flutter development, “general” isn’t enough. To build production-grade apps, you need an assistant that understands the nuance of localization, the latest Dart language features, and how to add integration tests. + +Today, we’re introducing **Agent Skills** for Flutter and Dart — a new way to give your AI tools domain-specific expertise. + +## Beyond the knowledge gap + +One of the primary challenges in AI development is the “knowledge gap.” Flutter and Dart can launch new features more quickly than LLMs can update their fixed training data. As a part of [how we are thinking about AI](https://medium.com/flutter/how-dart-and-flutter-are-thinking-about-ai-in-2026-e2fd64e1fdd0), we are looking for ways to not only address the knowledge gap but also ensure the agent applies that knowledge to achieve the task accurately and efficiently following the most optimal workflows. + +A little over a year ago, Model Context Protocols (MCP) were the way to provide more AI domain-specific expertise. While MCP gives an agent access to specialized tools, an Agent Skill teaches the agent *how* to use those tools for a specific task. Think of it this way: MCP provides the hammer and nails (the tools), while a Skill provides the blueprint and the professional know-how to build the house. + +Skills improve context efficiency through “progressive disclosure”. This is similar to how deferred loading works in Flutter, where apps can load libraries when needed, coding agents load Skills when they are relevant to what you’re trying to do . + +For Flutter and Dart, these Skills provide tailored instructions for common workflows, and enhance the tools provided in the Dart MCP server to reduce the knowledge gap, which improves accuracy and lowers token usage. + +## A task-oriented approach + +Our early experimentation revealed that Skills that only provide documentation don’t add as much value as we initially assumed. Since Flutter’s comprehensive and well written documentation is open-sourced, modern models are already highly capable of finding relevant information for most questions and tasks. + +So, we pivoted to creating Skills that are “task-oriented”. Every skill in our GitHub [Flutter Skills](https://github.com/flutter/skills) or [Dart Skills](https://github.com/dart-lang/skills) repositories focuses on developer tasks like building adaptive layouts- by providing instructions for agents to reliably complete the task. We have conducted extensive manual evaluations to define our initial set of launched skills, and are working on an automated evaluation pipeline that we will share soon. + +## Using the Skills + +To start using these Skills in your workflow, first install the Skill set in your project directory: + +```shell +npx skills add flutter/skills - skill '*' - agent universal +npx skills add dart-lang/skills - skill '*' - agent universal +``` + + +You will be asked to select the Skills you want to install. Pick all or select the specific ones you might find most useful. + +Then choose the agent that you prefer to develop with. + +Now, prompt your AI agent as usual. Here are 5 ways you can use these Skills today: + +**Skill #1**: `flutter-add-integration-test` + +Configures Flutter Driver for app interaction and converts MCP actions into permanent integration tests. + +``` +Add an integration test for the checkout flow in my app +``` + + +**Skill #2**: `flutter-setup-localiztion` + +Adds localization support to your Flutter project + +``` +Set up localization in my app +``` + + +**Skill #3**: `flutter-build-responsive-layout` + +Uses `LayoutBuilder`, `MediaQuery`, or `Expanded`/`Flexible` to create a layout that adapts to different screen sizes. + +``` +Ensure that the checkout screen uses repsonsive layout +``` + + +**Skill #4**: `dart-use-pattern-matching` + +Refactors code to use Dart’s pattern matching language capabilities where appropriate + +``` +Refactor my code so that it uses pattern matching where possible +``` + + +**Skill #5**: `dart-collect-coverage` + +Uses the `coverage` package to collect unit test coverage and generate an LCOV report. + +``` +Collect test coverage for my project +``` + + +For more prompt examples, check out the readme [Flutter Skills](https://github.com/flutter/skills) or [Dart Skills](https://github.com/dart-lang/skills) repositories on GitHub. + +## Tell us what you think + +These initial core Skills, designed to handle the most common Flutter development hurdles, are just the beginning. We want to build the future of AI-assisted development with you, our community. As you use these Skills and create new ones for your projects, file issues ([Dart Skills repo](https://github.com/dart-lang/skills/issues), [Flutter Skills repo](https://github.com/flutter/skills/issues)), and let us know what additional work you’d like to see. We look forward to helping improve your productivity as you use these Skills! \ No newline at end of file diff --git a/sites/www/content/blog/introducing-the-flutter-consulting-directory/images/00WsqrUDKa7Uikyh9.webp b/sites/www/content/blog/introducing-the-flutter-consulting-directory/images/00WsqrUDKa7Uikyh9.webp new file mode 100644 index 0000000000..69c7047f3a Binary files /dev/null and b/sites/www/content/blog/introducing-the-flutter-consulting-directory/images/00WsqrUDKa7Uikyh9.webp differ diff --git a/sites/www/content/blog/introducing-the-flutter-consulting-directory/images/07xi_G_tYKGROB8su.webp b/sites/www/content/blog/introducing-the-flutter-consulting-directory/images/07xi_G_tYKGROB8su.webp new file mode 100644 index 0000000000..d075106385 Binary files /dev/null and b/sites/www/content/blog/introducing-the-flutter-consulting-directory/images/07xi_G_tYKGROB8su.webp differ diff --git a/sites/www/content/blog/introducing-the-flutter-consulting-directory/images/0H35eVj4imixLHgDv.gif b/sites/www/content/blog/introducing-the-flutter-consulting-directory/images/0H35eVj4imixLHgDv.gif new file mode 100644 index 0000000000..86feef8c74 Binary files /dev/null and b/sites/www/content/blog/introducing-the-flutter-consulting-directory/images/0H35eVj4imixLHgDv.gif differ diff --git a/sites/www/content/blog/introducing-the-flutter-consulting-directory/images/1m5eYW0Qgmdse1bpkqbsPLQ.webp b/sites/www/content/blog/introducing-the-flutter-consulting-directory/images/1m5eYW0Qgmdse1bpkqbsPLQ.webp new file mode 100644 index 0000000000..36b46165da Binary files /dev/null and b/sites/www/content/blog/introducing-the-flutter-consulting-directory/images/1m5eYW0Qgmdse1bpkqbsPLQ.webp differ diff --git a/sites/www/content/blog/introducing-the-flutter-consulting-directory/images/1wgS4PLHJhlegD-knIZhEFA.webp b/sites/www/content/blog/introducing-the-flutter-consulting-directory/images/1wgS4PLHJhlegD-knIZhEFA.webp new file mode 100644 index 0000000000..80864b7f05 Binary files /dev/null and b/sites/www/content/blog/introducing-the-flutter-consulting-directory/images/1wgS4PLHJhlegD-knIZhEFA.webp differ diff --git a/sites/www/content/blog/introducing-the-flutter-consulting-directory/index.md b/sites/www/content/blog/introducing-the-flutter-consulting-directory/index.md new file mode 100644 index 0000000000..ef32ba44bf --- /dev/null +++ b/sites/www/content/blog/introducing-the-flutter-consulting-directory/index.md @@ -0,0 +1,65 @@ +--- +title: "Introducing the Flutter Consulting Directory" +description: "Where organizations can find consultants to help build their next application" +publishDate: 2023-09-13 +author: leighajarett +image: images/1m5eYW0Qgmdse1bpkqbsPLQ.webp +category: spotlight +layout: blog +--- + +Design agencies and digital technology consultancies play a vital role in the success of applications. Enterprises often look to consultancies to help them build out proof of concepts, re-design existing applications, or act as a subject matter expert for development projects. Startups look to agencies to build their brand identity and create minimum viable prototypes. These trusted partners can be difficult to find. + +That’s why we’re excited to launch the [Flutter Consultant Directory.](http://flutter.dev/consultants) + +<DashImage figure src="images/1m5eYW0Qgmdse1bpkqbsPLQ.webp" alt="flutter.dev/consultants" caption="flutter.dev/consultants" /> + + +We’re launching this new page to simplify finding trusted Flutter partners. We vet the partners listed in the directory to verify their record of successful Flutter projects, a sufficient number of skilled developers on staff, and their commitment to grow the Flutter community. + +## **Resources for consultants** + +As part of this launch, we’re highlighting resources to help prospective consultants ramp up their Flutter expertise. This includes: + +### [Flutter pitch deck](https://docs.google.com/presentation/d/1TblnxXMVO1c75ggdLnA4cPh_nsDVvT--RsFqUIN6Fvg/copy) + +*Make a copy and add in some studies from your own team to make a compelling Flutter case for potential clients* + +<DashImage figure src="images/07xi_G_tYKGROB8su.webp" alt="Visit the [link](https://docs.google.com/presentation/d/1TblnxXMVO1c75ggdLnA4cPh_nsDVvT--RsFqUIN6Fvg/copy) to make your own copy." caption="Visit the [link](https://docs.google.com/presentation/d/1TblnxXMVO1c75ggdLnA4cPh_nsDVvT--RsFqUIN6Fvg/copy) to make your own copy." /> + + +### Building Wonders with Flutter + +*Testimonial from a top Flutter design agency, gSkinner, on why Flutter has been a valuable toolkit for their team.* + +<YoutubeEmbed id="6IKhXXFFOuw" title="Build Wonders with Flutter" fullwidth="true"/> + + +### [How FlutterFlow makes consultants faster](https://medium.com/flutterflow-enterprise/how-flutterflow-helps-consultants-solve-clients-last-mile-of-digital-transformation-b6d8e7d3b49e) + +*Learn how consultancies can deliver high quality client projects faster with FlutterFlow. [Read the blog post](https://medium.com/flutterflow-enterprise/how-flutterflow-helps-consultants-solve-clients-last-mile-of-digital-transformation-b6d8e7d3b49e).* + +<DashImage figure src="images/0H35eVj4imixLHgDv.gif" /> + + +### [**How IBM Created their Flutter Center of Excellence**](https://medium.com/flutter/how-ibm-is-creating-a-flutter-center-of-excellence-3c6a3c025441) + +*Learn why and how IBM built out a Flutter Center of Excellence with hundreds of Flutter developers. [Read the blog post](https://medium.com/flutter/how-ibm-is-creating-a-flutter-center-of-excellence-3c6a3c025441).* + +<DashImage figure src="images/1wgS4PLHJhlegD-knIZhEFA.webp" /> + + +### [Training Clients on Flutter](http://verygood.ventures/blog/training-clients-in-flutter) + +*Learn how VGV created their successful Flutter training program. You can up skill your own developers, or your clients.* [*Read the blog post*](https://verygood.ventures/blog/training-clients-in-flutter). + +<DashImage figure src="images/00WsqrUDKa7Uikyh9.webp" /> + + +## **Ready to apply?** + +To apply, agencies and consultancies should complete [this Google form](https://docs.google.com/forms/d/1CS1Ysbwyeya4o0kzu1acPFie8ozHrUgEmAK4kx0_MZM/edit?usp=drive_open&resourcekey=0-Qi5RvEWxp_WT86zHombxgg). We review submissions on a periodic basis and will inform you of your status in due course. We ask consultants to update their information on a quarterly basis to ensure the accuracy and relevance of the directory. + +## **Have feedback?** + +We’re always eager for feedback. If you have ideas on how to improve the directory or partner resources, send us a note at [flutter-partner-program@google.com](mailto:flutter-partner-program@google.com). \ No newline at end of file diff --git a/sites/www/content/blog/introducing-the-flutter-meetup-network/images/14eNp-BypLrDgEwkyWbm2zw.webp b/sites/www/content/blog/introducing-the-flutter-meetup-network/images/14eNp-BypLrDgEwkyWbm2zw.webp new file mode 100644 index 0000000000..b4af90cb5d Binary files /dev/null and b/sites/www/content/blog/introducing-the-flutter-meetup-network/images/14eNp-BypLrDgEwkyWbm2zw.webp differ diff --git a/sites/www/content/blog/introducing-the-flutter-meetup-network/images/1v_2ToStDprKx9KEzZgA5Fw.webp b/sites/www/content/blog/introducing-the-flutter-meetup-network/images/1v_2ToStDprKx9KEzZgA5Fw.webp new file mode 100644 index 0000000000..80adb2ed06 Binary files /dev/null and b/sites/www/content/blog/introducing-the-flutter-meetup-network/images/1v_2ToStDprKx9KEzZgA5Fw.webp differ diff --git a/sites/www/content/blog/introducing-the-flutter-meetup-network/index.md b/sites/www/content/blog/introducing-the-flutter-meetup-network/index.md new file mode 100644 index 0000000000..73056fb538 --- /dev/null +++ b/sites/www/content/blog/introducing-the-flutter-meetup-network/index.md @@ -0,0 +1,27 @@ +--- +title: "Introducing the Flutter Meetup Network!" +description: "Sarah Fullmer tells us about support for Flutter meetups." +publishDate: 2021-10-01 +author: sfshaza2 +image: images/14eNp-BypLrDgEwkyWbm2zw.webp +category: announcements +layout: blog +--- + +<DashImage figure src="images/14eNp-BypLrDgEwkyWbm2zw.webp" alt="Join forces with other Flutter enthusiasts!" caption="Join forces with other Flutter enthusiasts!" /> + + +The Flutter Meetup Network (FMN) is an international network of Meetup groups united by their enthusiasm for Flutter. The FMN program mission is ***to foster a thriving worldwide community of Flutter developers by empowering community organizers to educate and inspire local communities with engaging events.*** + +Meetups — and similar developer community groups — are a great resource. As Flutter has grown in popularity over the past few years, over 100 Meetup groups have sprung up organically around the world to celebrate and educate their members about Flutter. The volunteers who run these groups have hosted *many* awesome events and workshops. + +<DashImage figure src="images/1v_2ToStDprKx9KEzZgA5Fw.webp" /> + + +We in the Flutter team see and appreciate these amazing communities. 💙 The Flutter Meetup Network (FMN) is now being launched to support these groups officially. Network members will have access to various resources (more details available soon!), making it easier to plan and host Flutter-themed events. + +Meetups have many benefits, aside from networking. When stuck on an issue in your current Flutter project, chances are good that you can find a developer in your local Flutter Meetup who has solved a similar problem. Or, maybe they know the perfect package for your needs. + +We are thrilled to support our amazing and passionate organizers and can’t wait to see what the Flutter Meetup Network does moving forward. + +[Join a Meetup near you!](https://www.meetup.com/pro/flutter) \ No newline at end of file diff --git a/sites/www/content/blog/introducing-the-flutter-news-toolkit-now-accepting-applications/images/0NfASfTMOVQde8r-K.webp b/sites/www/content/blog/introducing-the-flutter-news-toolkit-now-accepting-applications/images/0NfASfTMOVQde8r-K.webp new file mode 100644 index 0000000000..15980a0399 Binary files /dev/null and b/sites/www/content/blog/introducing-the-flutter-news-toolkit-now-accepting-applications/images/0NfASfTMOVQde8r-K.webp differ diff --git a/sites/www/content/blog/introducing-the-flutter-news-toolkit-now-accepting-applications/images/0TC5XvMCLwYWLzFhG.webp b/sites/www/content/blog/introducing-the-flutter-news-toolkit-now-accepting-applications/images/0TC5XvMCLwYWLzFhG.webp new file mode 100644 index 0000000000..aee2b8bc44 Binary files /dev/null and b/sites/www/content/blog/introducing-the-flutter-news-toolkit-now-accepting-applications/images/0TC5XvMCLwYWLzFhG.webp differ diff --git a/sites/www/content/blog/introducing-the-flutter-news-toolkit-now-accepting-applications/images/1eQsCJ1lXurwLVnD5wIDxkg.webp b/sites/www/content/blog/introducing-the-flutter-news-toolkit-now-accepting-applications/images/1eQsCJ1lXurwLVnD5wIDxkg.webp new file mode 100644 index 0000000000..8810ef81f4 Binary files /dev/null and b/sites/www/content/blog/introducing-the-flutter-news-toolkit-now-accepting-applications/images/1eQsCJ1lXurwLVnD5wIDxkg.webp differ diff --git a/sites/www/content/blog/introducing-the-flutter-news-toolkit-now-accepting-applications/index.md b/sites/www/content/blog/introducing-the-flutter-news-toolkit-now-accepting-applications/index.md new file mode 100644 index 0000000000..de0d040b2f --- /dev/null +++ b/sites/www/content/blog/introducing-the-flutter-news-toolkit-now-accepting-applications/index.md @@ -0,0 +1,98 @@ +--- +title: "Introducing the Flutter News Toolkit — now accepting applications" +description: "Be among the first to launch news mobile applications with the Flutter News Toolkit" +publishDate: 2022-11-10 +author: zoeyfan +image: images/1eQsCJ1lXurwLVnD5wIDxkg.webp +category: events +layout: blog +--- + +## Introducing the Flutter News Toolkit — now accepting applications + +The way we consume the news has changed considerably over the last twenty years. Market research suggests that [US smartphone users spend 88% of their time using apps versus mobile web browsers](https://www.insiderintelligence.com/content/the-majority-of-americans-mobile-time-spent-takes-place-in-apps). However, for news publishers that are often resource-constrained, mobile apps can be difficult and costly to build. + +Building a high-quality mobile news app is a major undertaking: you want to include a rich reading experience, great navigation and search, authentication, ad integration, notifications, profiles and subscriptions, and a tailored design experience. Moreover, you have to do all that for multiple platforms (such as iOS and Android), continue to update your app with the latest features, and keep multiple products in sync. This is a tremendous effort for large news organizations with a portfolio of brands *and* for smaller publishers who can’t afford to staff large app development teams that specialize in each platform. + +We’ve invested in helping news organizations solve this problem, using industry best practices and a powerful new toolkit from Google. + +### Introducing the Flutter News Toolkit + +We’re excited to announce that Google’s Flutter team is partnering with the [Google News Initiative (GNI)](https://newsinitiative.withgoogle.com/) to release[ a free toolkit](https://forms.gle/J6t3Auz4QoZTunBJ7) that allows publishers to create mobile apps on iOS and Android faster by reducing the app development time up to 80%. + +The Flutter News Toolkit is an easy-to-use template on which a news organization can build an app and includes blocks of sample code for common user interface and app functionalities. + +We worked closely with industry experts and news publishers across the world to understand the most critical features and user workflows for a news application, and incorporated the best practices from [News Consumer Insights ](https://news-consumer-insights.appspot.com/)to help you achieve higher reader engagement and meet financial goals through the Flutter news toolkit. + +The Flutter News Toolkit includes critical features such as: + +* User onboarding + +* Account creation/login + +* Content feeds and content pages + +* Analytics + +* Notifications + +* Social sharing + +* Subscriptions + +* Ads + +You can use these pre-integrated features out of the box, or easily modify and swap them with other functionalities that you prefer. + +<DashImage figure src="images/1eQsCJ1lXurwLVnD5wIDxkg.webp" alt="*Sample features provided in the Flutter News Toolkit*" caption="*Sample features provided in the Flutter News Toolkit*" /> + + +### Early adopter of the Flutter News Toolkit + +One of the Flutter News Toolkit’s early adopters is Hespress, which is one of Morocco’s largest news sites. Hespress had been wanting to reach more audiences by creating a mobile application targeting English speakers, but didn’t have enough resources to get started. With the Flutter News Toolkit, Hespress was able to finish the entire application development within 6 weeks and they have recently launched their app [Hespress](https://apps.apple.com/us/app/hespress-english/id1609463589) [English](https://play.google.com/store/apps/details?id=com.hespress.english&hl=en_US&gl=US). + +“The Flutter News Toolkit was intuitive to work with and customized to look and feel like a proper Hespress app,” says El Guennouni Hassane, the General Manager of Hespress. “It contained all the core features, sections, and layouts that would be required for us. It helped us save months of development and optimized the whole process of revamping our English app while maintaining one code base instead of two.” + +<DashImage figure src="images/0TC5XvMCLwYWLzFhG.webp" alt="*Screenshots of Hespress English app*" caption="*Screenshots of Hespress English app*" /> + + +### Apply now to access the Flutter News Toolkit + +As we roll out the Flutter News Toolkit, we are offering limited spaces to publishers who are interested in launching news mobile apps within the next 3–6 months. This program is open for application to all publishers across the world and in any language. We will be expanding our program over time, but here are some benefits you will get by joining now: + +* Access to all of the source code and a step-by-step guide for the Flutter News Toolkit for free + +* The ability to modify, delete, or add new features as you wish + +* Assistance from Google in launching your custom app in the Play Store and Apple Store + +* The opportunity to be featured in Google’s marketing materials + +Please fill out the [application](https://forms.gle/J6t3Auz4QoZTunBJ7). We’re accepting new publishers in batches as we refine the product before launching to general availability in the coming months. We look forward to helping more news organizations expand their digital presence, establish audience relationships, and build sustainable business models with diversified revenue streams. + +## FAQ + +### What is Flutter? + +Flutter is Google’s open source UI toolkit for building beautiful apps for mobile, web, and desktop from a single codebase. Flutter significantly reduces the time required to build and publish an app. Today, Flutter is [the most popular cross-platform](https://www.statista.com/statistics/869224/worldwide-software-developer-working-hours/) development framework, with over 3 million developers around the world. There are more than 500,000 Flutter apps, including Alibaba, BMW, Google Pay, PUBG, Shein, and Wechat. + +### Is Flutter easy to learn? + +Yes, for developers who have knowledge of other programming languages such as Java, learning Flutter is quick. Besides, we have a wealth of [learning resources](https://flutter.dev/learn) available for you to get started. + +### **Can I use other non-Google services such as Ads, analytics, and subscriptions in the app?** + +Of course! The Flutter News Toolkit only aims to provide a sample. You are not required to use any service you don’t want. In fact, it is easy to remove or swap the existing integrations with other services. For example, if you want to use non-Google Ads services, you can find other [packages](https://pub.dev/packages?q=platform%3Aandroid+platform%3Aios+ads) on pub.dev. + +### How much work am I required to do to launch the app? + +While the news template can lower the barrier to entry for building news mobile applications, this solution still requires Flutter development work. The template doesn’t support no-code or wysiwyg functionality or tools (such as dashboards) for non-technical users to modify the app branding and styling. + +We have mapped out the deployment process at a high-level with an assumption of 2–3 Flutter engineers working on the project. As an example, early adopters like Hespress finished their application development within 6 weeks. + +<DashImage figure src="images/0NfASfTMOVQde8r-K.webp" alt="*Deployment process at a high-level*" caption="*Deployment process at a high-level*" /> + + +### If I don’t have an in-house development team, how can I use the template? + +We can recommend Flutter agencies who can help you take the template to the finish line. As a disclaimer, Google is not associated with these agencies, and we do not take any revenue share from them. \ No newline at end of file diff --git a/sites/www/content/blog/issues-bugs-and-backlogs/images/19ZXPKWTBT01b_6zsFtundg.webp b/sites/www/content/blog/issues-bugs-and-backlogs/images/19ZXPKWTBT01b_6zsFtundg.webp new file mode 100644 index 0000000000..d777a788ba Binary files /dev/null and b/sites/www/content/blog/issues-bugs-and-backlogs/images/19ZXPKWTBT01b_6zsFtundg.webp differ diff --git a/sites/www/content/blog/issues-bugs-and-backlogs/images/1D_MCtnl2h_spFgoORikDcA.webp b/sites/www/content/blog/issues-bugs-and-backlogs/images/1D_MCtnl2h_spFgoORikDcA.webp new file mode 100644 index 0000000000..73bc0ea2bb Binary files /dev/null and b/sites/www/content/blog/issues-bugs-and-backlogs/images/1D_MCtnl2h_spFgoORikDcA.webp differ diff --git a/sites/www/content/blog/issues-bugs-and-backlogs/index.md b/sites/www/content/blog/issues-bugs-and-backlogs/index.md new file mode 100644 index 0000000000..34a4b7c42a --- /dev/null +++ b/sites/www/content/blog/issues-bugs-and-backlogs/index.md @@ -0,0 +1,89 @@ +--- +title: "Issues, Bugs, and Backlogs" +description: "Managing feedback in a large open source project like Flutter" +publishDate: 2019-11-25 +author: kf6gpe +image: images/1D_MCtnl2h_spFgoORikDcA.webp +category: tutorial +layout: blog +--- + +The growth of Flutter as a community-driven open source project is an ongoing source of delight to us here at Google. Whether measured in [popularity on GitHub](https://twitter.com/timsneath/status/1189594253690691584), number of [projects created](https://www.reddit.com/r/FlutterDev/comments/dsnzbq/githubs_2019_octoverse_report_is_out_flutter_is/), or [skills growth](https://adtmag.com/articles/2019/03/29/linkedin-skills.aspx), 2019 has been a defining year for the project, and we’re ever thankful to you all for your support and contributions to make Flutter the project it is. We build Flutter with you and for you, and we hope that comes across in everything we do. + +As we’ve grown, the number of issues we need to track grows, too! We use issues on GitHub in a variety of ways: not just to report bugs, but any unit of work pertaining to the project. Anyone can submit an issue, and these issues fall into a number of categories: + +* Feature requests: things a user or a contributor wish that Flutter could do, or do better. + +* Support issues: questions from users as to how Flutter works or how to do things. While these are better asked on Stack Overflow, many point to ways that we could improve our documentation, too. + +* Bona fide defects: things that don’t work as they should. This includes serious issues like crashes and performance regressions, but also those pesky fit-and-finish bugs that are common in any large software system. + +* Untriaged items that haven’t been examined and labeled. + +Thus, our issues aren’t just a collection of defects; they’re our backlog. Anything we think of that we may want to work on should be represented as an issue, appropriately labeled, so that as we plan our goals and milestones we have a place from which to start. + +Some projects use the number of open bugs as a proxy for gauging release quality, and classify bugs as either defects or issues. [Flutter doesn’t do this; we choose to track bugs and issues the same way, in GitHub, keeping everything in the open](https://github.com/flutter/flutter/wiki/Issue-hygiene#issue-philosophy). As a result, there will always be far more things that we *could* be working on than we actually *are* working on. This is true for many healthy open-source projects; one need only look at others that have been around for a while such as [Tensorflow](https://github.com/tensorflow/tensorflow/issues), [Chrome](https://bugs.chromium.org/p/chromium/issues/list), [Dart](https://github.com/dart-lang/sdk/issues), [Go](https://github.com/golang/go/issues), or [VSCode](https://github.com/Microsoft/vscode/issues). + +Nonetheless, we want to be sure that our incoming issues are well-labeled and do the necessary gardening work so that our issue database is an accurate reflection of our product status. To this end, we’ve engaged with the community: there are a number of volunteers helping us with our front-line triage, and we’re [pleased to announce that we’ve partnered with Nevercode](https://blog.codemagic.io/flutter-and-codemagic-join-forces-on-github/), the providers of [Codemagic](https://codemagic.io/), a leading CI/CD system for Flutter, to help us with our front-line triage as well. + +## The Lifecycle of an Issue + +We welcome new issue submissions! Our issue triage process is described in [Guidelines for triage](https://github.com/flutter/flutter/wiki/Triage). Here’s how it works in practice: + +An issue starts with you: a user of Flutter, or an open-source contributor. Once you file it (hopefully with a reproducible case if it’s a bug, or a clear outline of what you’re proposing and why, if it’s a feature request), it goes to *front-line triage*. + +In front-line triage, a community participant — perhaps a volunteer, perhaps someone at Nevercode, or an engineer working on Flutter — examines the issue you’ve filed, and asks several questions: + +* Is it clearly defined? + +* If a bug, does it have a reproducible case and enough to go on? + +* If a feature enhancement, do we understand enough about what you’re asking for so that we can gauge its contribution to the platform? + +* Does it duplicate a previously filed issue? + +As we do this, our front-line triage applies as many labels to the issue as we can. We use these labels in lots of ways, such as to establish relative priority, determine how to route an issue to a particular team, and whether to include the issue in an upcoming milestone. We use both GitHub queries and some custom tools to mine the issues for signals as to what the community is looking for and why. + +From there, it goes to *secondary triage*, where an assigned team looks at the issue, and asks questions such as how the issue fits with the current work that they’re doing, and when they can schedule the work for completion. Again, more labels may be added or changed, as the labels provide us with the best possible snapshots as to the work we want to do and who can do the work. + +Eventually, an engineer offers to work on the issue. Unlike many software projects, we generally don’t assign issues to contributors: whether a contributor works for Google or not, contributors offer to tackle issues, rather than get assigned issues from the backlog. By letting developers offer, we distribute load-balancing down to individual contributors; each contributor self-assigns only the issues they’re actively working on, and gives milestones to those issues so that we know when they’ll land on the master branch. Leads may ask particular people to work on issues, of course, but most of the time it’s just that: a request for help, not an ultimatum or an assignment. + +Once an issue has been completed to our satisfaction, it is closed. Often this includes a link to the pull request addressing the issue, but there are other reasons we close issues, including: + +* Is this a request for support? We direct the reporter to a support channel such as the [flutter-dev@googlegroups.com](mailto:flutter-dev@googlegroups.com) mailing list, the [r/FlutterDev](https://reddit.com/r/flutterdev) Reddit, our Discord communities ([user chat](https://discordapp.com/invite/N7Yshp4), [community chat](https://github.com/flutter/flutter/wiki/Chat) used primarily by contributors), or [Stack Overflow](https://stackoverflow.com/tags/flutter). + +* Is this a duplicate issue? Before closing, we link to the original issue, which also updates the original issue. + +* Does the issue have enough information to reproduce, and have we been able to reproduce it? If not, odds are that we can’t do anything and will close the issue. + +## Our Progress Thus Far + +As you can imagine, as our popularity has grown, so has the number of opened and closed issues for [github.com/flutter/flutter](http://github.com/flutter/flutter) (this contains all issues except those for the web site, which are logged at [github.com/flutter/website](http://github.com/flutter/website)): + +<DashImage figure src="images/1D_MCtnl2h_spFgoORikDcA.webp" alt="Closed vs. open bugs for month-ends since Jan-2018" caption="Closed vs. open bugs for month-ends since Jan-2018" /> + + +Equally exciting is the number of issues *not* bearing a label corresponding to one of our secondary triage teams, such as [framework](https://github.com/flutter/flutter/labels/framework), [engine](https://github.com/flutter/flutter/labels/engine), or [plugin](https://github.com/flutter/flutter/labels/plugin): + +<DashImage figure src="images/19ZXPKWTBT01b_6zsFtundg.webp" alt="Issues not labeled for secondary triage, Mar-2019-present" caption="Issues not labeled for secondary triage, Mar-2019-present" /> + + +You can clearly see where we started to engage Nevercode in our triage process, first in early September, with great progress by mid-September as they got up to speed. + +This emphasis on triage reflects our goal: not to have zero *open* issues, but to have zero *unlabeled* issues to provide adequate signals from our community, so that we can best prioritize our work. As Flutter continues to grow in popularity, we expect to see continued growth in the number of open issues that require triage, many of which will be requests for new features from the community. We’ll continue to use labels to help us determine which are bugs that require immediate attention, which are bugs we can defer to our next beta or stable release, and which are new feature requests. + +## How You Can Help + +You can help keep our issue database clean and tidy with actionable issues. When filing an issue, please consider the following : + +* Don’t use GitHub for support. As part of our issue triage, we’re now closing requests for support that come up in GitHub, redirecting folks to better channels. In those channels you’ll likely find someone able to answer your question, and the questions and answers are easier for other users to find as well. + +* Help with reproducible cases: if it’s your own issue, make sure you have one. Even if it’s not your issue, we’ll gladly take reproducible cases from you, too! Writing reproducible cases for bugs is a great way to start learning how to use Flutter with real-world examples. + +* Update and upvote issues that are important to you. + +* Contribute test cases to the Flutter repository. This is another way to get your feet wet using Flutter and join the community. It also helps us prevent regressions: while all new code is accompanied by test cases, we strive to increase our test coverage as a matter of good practice. If you’re interested in contributing in this way, you can take a look at issues with the [a: tests](https://github.com/flutter/flutter/labels/a%3A%20tests) label. + +* Consider helping us triage issues that others have filed. + +We’re deeply appreciative of the support and trust you’ve given us in investing your time and application ideas with Flutter. We welcome community involvement, whether it be filing a bug or feature you’d like to see addressed, or creating a pull request to make Flutter the platform you want to work with. Thank you! \ No newline at end of file diff --git a/sites/www/content/blog/its-time-the-flutter-clock-contest-results/images/10z33rzzkJfYqORqmhtjdMg.jpeg b/sites/www/content/blog/its-time-the-flutter-clock-contest-results/images/10z33rzzkJfYqORqmhtjdMg.jpeg new file mode 100644 index 0000000000..b421b7ff6c Binary files /dev/null and b/sites/www/content/blog/its-time-the-flutter-clock-contest-results/images/10z33rzzkJfYqORqmhtjdMg.jpeg differ diff --git a/sites/www/content/blog/its-time-the-flutter-clock-contest-results/index.md b/sites/www/content/blog/its-time-the-flutter-clock-contest-results/index.md new file mode 100644 index 0000000000..2d4993295b --- /dev/null +++ b/sites/www/content/blog/its-time-the-flutter-clock-contest-results/index.md @@ -0,0 +1,111 @@ +--- +title: "🕒 It’s Time: The Flutter Clock Contest Results" +description: "We are always amazed by what developers around the world are building with Flutter. This year we partnered with the Google Assistant and…" +publishDate: 2020-02-25 +author: aguinis +image: images/10z33rzzkJfYqORqmhtjdMg.jpeg +category: events +layout: blog +--- + +We are always amazed by what developers around the world are building with Flutter. This year we partnered with the Google Assistant and Lenovo teams to bring you our most recent contest: [Flutter Clock](http://flutter.dev/clock) — challenging you to build a Flutter clock face application for the [Lenovo Smart Clock](https://www.lenovo.com/us/en/smart-clock) that is beautiful and innovative. + +**We received over 850 unique submissions across 86 different countries.** This included entries from first time coders to Flutter experts. The following video provides a glimpse of what you made from the hundreds of submissions we received: + +<YoutubeEmbed id="PaPUkxYHDUw" title="Flutter Clock Highlight Reel" fullwidth="true"/> + + +## The winners are… 🥁🥁🥁 + +### **Grand Prize Winner: [Particle Clock](https://github.com/miickel/flutter_particle_clock) by Mickel Andersson** + +<iframe src="https://media.giphy.com/media/MES3zoI1GUgMYB2qnu/giphy.gif" width="435" height="261" frameborder="0" allowfullscreen></iframe> + + +Judges were in awe from the visual effect, execution, code quality and overall novelty of this clock. The mathematical complexity of particle calculation was impressive. + +### **Visual Beauty Winner: [Cloom Clock](https://github.com/OldMetalmind/cloom_clock) by Filipe Barroso and Francisco Frutuoso** + +<iframe src="https://media.giphy.com/media/KB1Xj416G8LJszyQan/giphy.gif" width="435" height="255" frameborder="0" allowfullscreen></iframe> + + +The custom type and elegant animations made this clock a visual stand out. They used [Rive](https://rive.app/) for excellent animations, and had a vibrant color selection. + +### **Overall Execution Winner: [Humanbeans Clock](https://github.com/brestnichki/humanbeans-clock) by Boris Brestnichki** + +<iframe src="https://media.giphy.com/media/kdcqAfxWpSMQ8EF9Kl/giphy.gif" width="435" height="244" frameborder="0" allowfullscreen></iframe> + + +This submission has a great storyline and thought out UX. The attention to detail, including custom illustration and hand drawn numerals, made this clock stand out. + +### **Code Quality Winner: [Creative Creator Or Maybe Not Clock](https://github.com/creativecreatorormaybenot/clock) by Hubert Henkemeier** + +<iframe src="https://media.giphy.com/media/IzzvHEhJQbaNuWFQcd/giphy.gif" width="435" height="261" frameborder="0" allowfullscreen></iframe> + + +It’s impressive how well documented and written this clock is. The participant even [authored a detailed post](https://medium.com/flutter-community/pure-flutterclock-face-every-line-customly-drawn-with-pixel-perfect-control-c27cba427801?_branch_match_id=653740167174388407) describing their full process. Everything is painted purely with Flutter’s Canvas: no assets, no packages, and no prebuilt widgets were used. + +### **Novelty of Idea Winner: [Sunset Reflections Clock](https://github.com/vmorilla/sunset_reflections_clock) by Victor Morilla** + +<iframe src="https://media.giphy.com/media/XClwr7rqCItUOhDI4i/giphy.gif" width="435" height="244" frameborder="0" allowfullscreen></iframe> + + +Interesting idea to emulate the behavior of cold cathode displays. The rendering and way this clock was created was novel. + +## Honorable Mentions: + +Judging was no small task, and our judges found it difficult to choose a mere 5 top winners out of all the entries based on our criteria. Therefore, our judges also awarded honorable mentions to the following entries, in alphabetical order: + +* [Agent Clock](https://github.com/kruegerrobotics/flutter_agent_clock) by Thomas Krueger + +* [Animal Clock](https://github.com/seiwonlee/aninmal_clock) by Sei Lee + +* [Ants Clock](https://github.com/StuartApp/Flutter-clock-challenge) by Pau Picas Sans + +* [BarBar Clock](https://github.com/aednlaxer/flutter-clock-challenge-barbar) by Aleksandr Troshkov + +* [Boating Clock](https://github.com/SpiciedCrab/Boating) by Yi He + +* [Circle Clock](https://github.com/NotThatBowser/flutter_clock) by Max Bowser + +* [DJ Clock](https://github.com/EriaWist/flutter_clock.git) by Bao Hui Huang + +* [Dots Clock](https://github.com/kainsteffen/dots-clock) by Tran Duy Khanh Steffen + +* [Flutter Clock](https://github.com/meszarosdezso/flutter_clock.git) by Dezso Meszaros + +* [Flutter Clock](https://github.com/orestesgaolin/flutter_clock) by Dominik Roszkowski + +* [Flutter Clock](https://github.com/HelgeWieding/helg9000) by Helge Wieding + +* [Flutter Clock](https://github.com/mono0926/flutter_clock) by Masayuki Ono + +* [Flutter Clock](https://github.com/sla-000/flutter_clock) by Vyacheslav Ryabinin + +* [Freehand Clock](https://github.com/thayesx/freehand_clock) by Tyler Hayes + +* [Generative Clock](https://github.com/Fabian-Stein/flutterClock) by Fabian Stein + +* [Infinity Clock](https://github.com/AkashDivya/infinity_flutter_clock) by Akash Divya + +* [Iso Clock](https://github.com/cookmscott/FlutterClockChallenge) by Scott Cook + +* [Lofi Clock](https://github.com/nosliwmichael/flutter_clock) by Michael Wilson + +* [Matches Clock](https://github.com/MikolajLen/matches_clock) by Mikołaj Lenart + +* [Paper Clock](https://github.com/ryanjacktaylor/paper_clock) by Ryan Taylor + +* [Scenery Clock](https://github.com/Dwite/Scenery-FlutterClock) by Valerii Kuznietsov + +* [Snake Clock](https://github.com/GilDev/SnakeClock) by Gilles Devillers + +* [Star Clock](https://github.com/CiriousJoker/star_clock) by Philipp Bauer + +* [Steampunk Clock](https://github.com/tsinis/flutter_clock) by Roman Cinis + +* [Tetris Time Clock](https://github.com/msoftware/flutter_clock) by Michael Jentsch + +We were amazed by the quality and creativity of the entries — there are so many more wonderful clocks [that you can see here](https://twitter.com/search?q=%23flutterclock&src=typed_query). Thanks so much for participating! + +<DashImage figure src="images/10z33rzzkJfYqORqmhtjdMg.jpeg" /> diff --git a/sites/www/content/blog/jaimes-build-context-a-flutter-developer-s-thoughts-about-antigravity-tips/images/1iTTaVFKTZVHVl3ykFCmjyw.jpeg b/sites/www/content/blog/jaimes-build-context-a-flutter-developer-s-thoughts-about-antigravity-tips/images/1iTTaVFKTZVHVl3ykFCmjyw.jpeg new file mode 100644 index 0000000000..0b0473e234 Binary files /dev/null and b/sites/www/content/blog/jaimes-build-context-a-flutter-developer-s-thoughts-about-antigravity-tips/images/1iTTaVFKTZVHVl3ykFCmjyw.jpeg differ diff --git a/sites/www/content/blog/jaimes-build-context-a-flutter-developer-s-thoughts-about-antigravity-tips/index.md b/sites/www/content/blog/jaimes-build-context-a-flutter-developer-s-thoughts-about-antigravity-tips/index.md new file mode 100644 index 0000000000..cb605c7e2f --- /dev/null +++ b/sites/www/content/blog/jaimes-build-context-a-flutter-developer-s-thoughts-about-antigravity-tips/index.md @@ -0,0 +1,95 @@ +--- +title: "Jaime’s build context: A Flutter developer’s thoughts about Antigravity + Tips!" +description: "Hi, I’m Jaime Wren, a long-time developer tooling software engineer on the Flutter team. I’ve seen many shifts in the software industry…" +publishDate: 2025-12-09 +author: jwren +image: images/1iTTaVFKTZVHVl3ykFCmjyw.jpeg +layout: blog +--- + +<DashImage figure src="images/1iTTaVFKTZVHVl3ykFCmjyw.jpeg" /> + + +Hi, I’m Jaime Wren, a long-time developer tooling software engineer on the Flutter team. I’ve seen many shifts in the software industry over the years, but the whole industry has just stepped into a new Wild West at breakneck speed and this is both exciting and nerve-wracking. + +In my opinion, we developers are complicated creatures driven by two forces: the need for productivity with our tasks and our inherent joy of programming itself. We thrive when we can slip into a fast, uninterrupted loop. To realize our full potential as developers, we need our entire toolchain to come together without introducing friction. As we transition into the Agentic Era, this need for a cohesive loop is non-negotiable; this is where Flutter has a distinct advantage. + +I’ve tested a lot of AI tools these last few years, waiting for something that actually will save Flutter developers time rather than just shifting where time is spent. Nothing turned the corner for me until [Antigravity](https://antigravity.google/), Google’s new Agentic IDE. It bridges the gap between raw model capability and actual engineering utility. I can finally see all of the infrastructure and tooling around the LLMs coming together to remove friction, rather than adding to it. + +From my own testing, Flutter isn’t just “compatible” with AI tools like Antigravity; it is uniquely equipped to power them. Why? It comes down to Flutter’s strict structures and robust tooling. A core philosophy of Antigravity is a reliance on verification to know if a piece of code actually works. Flutter’s tools provide the immediate feedback that Antigravity’s agents need to validate these actions. All it takes to get started are a few settings to [configure](https://docs.flutter.dev/ai/mcp-server#antigravity) extensions and the MCP server. + +## Some cool things you can do + +There are several cool things that I and other members of the Flutter community have learned to do with Antigravity to speed our development process. I’ve listed a few of them below, but I request that if you have any additional tips, please add them in the comments attached to this post. + +### Run tests & fix the problematic code + +You can use Antigravity to run existing tests, fix any problematic code that is breaking your tests, and then verify that the tests are passing again. Here is the prompt that I use: + +```plaintext +Execute the full test suite with `flutter test`. If failures occur, +fix them one by one. After applying each fix, re-run the full suite one +last time to ensure no regressions. +``` + + +### Fix errors and warnings + +To prepare a PR before pushing it, you can use Antigravity to fix your errors, warnings and lints. Here is my prompt for this: + +```plaintext +Run `flutter analyze` over my project. If it fails, fix any errors and +warnings, then validate that they are fixed. +``` + + +### Discover lints and use them + +There are a lot of lints out there but I don’t have time to research them all and figure out which ones are best for each project. I discovered that this cool prompt helps Antigravity quickly scan my project, suggest, and enable lints that make sense for it: + +```plaintext +Read https://dart.dev/tools/linter-rules and identify rules I am +implicitly following but haven't enabled. Add them to analysis_options.yaml, +run flutter analyze, and fix any resulting violations. +``` + + +### Discover good pub.dev packages for my project + +There are tons of pub.dev packages out there, and like the lints, researching them takes time and effort. I wanted a way to figure out which ones might be best for my projects while not leaving my IDE. This prompt worked well for me: + +```plaintext +I need preferences to be shared from one instance of my app to the next, +please go search for this feature in https://pub.dev/, then run +`flutter pub get`, validate that the pub status is in a good status. +Then, find an example in my project to use this new package, +fix any issues that appear from `flutter analyze`, and fix them. +Finally, add a test for the new feature. +``` + + +## Thoughts about my journey with Flutter over the years and why I’m excited + +I remember when Flutter entered the scene about 10 years ago. From the beginning and in my opinion, Flutter brought together the right language, framework and tools to be usable-by-default. An often overlooked factor in Flutter’s success is that these pieces weren’t bolted together from different vendors. Instead, the framework, rendering engine, CLI tooling, IDE tooling, language and ecosystem have always been driven by the same organization that has enabled these teams to coordinate to enable the usable-by-default cohesive vision. It’s because of this that I believe there is a “I finally get Hot Reload” moment for every new Flutter developer that makes them excited and hopeful because: + +* The Flutter infrastructure is capable of patching code in milliseconds. + +* The errors and warnings appear as you type and ensure that code is valid before a save occurs. + +* The language helps prevent syntax errors and frictions. + +* Obvious overflow errors are displayed as yellow and black stripes that signal layout issues that need to be addressed. + +In the past year, the promise of generative AI in software development has been akin to the Wild West–demos and products have shown a lot of progress, but have not followed the usable-by-default mantra that has been core to Flutter’s success. With model improvements such as larger context windows that allow a model to actually read a project, and with concepts in the space converging, frameworks like Flutter have had an entry point to add value to agents and workflows. The Flutter team followed suit this year with the [GenUI](https://docs.flutter.dev/ai/genui) effort, the [Dart and Flutter MCP Server](https://docs.flutter.dev/ai/mcp-server), and [AI rules](https://docs.flutter.dev/ai/ai-rules) suggestions. + +Now, with Antigravity entering the arena, we have something that is more than a chatbot built into a window in your IDE. **Antigravity inhabits the IDE**, running commands and using knowledge about other Flutter and Dart projects to correctly follow your direction and take actions on your behalf in the project space. + +As Flutter engineers, we take for granted the ability to know how to immediately run static analysis (`dart analyze`) or launch an app (`flutter run`) in a project we’ve never seen before. While none of these restrictions have limited what developers could *create* with Flutter, the natural consequence with LLMs and agentic tooling is that our collective uniformity gives these interfaces a huge leap forward in understanding and acting on the structure of our projects. + +To reap the benefits of LLM tooling, agents require a verification through a positive feedback loop. If a model is producing value on every tenth piece of output and hallucinating the other nine instances, I might be amused, but I won’t be using the tool again. With Antigravity, *verification* is a core philosophy designed to give agents a feedback loop, allowing them to iterate before finishing work. This is where the benefit of existing robust Flutter tools comes into play, when using Antigravity to assist in writing code, the agent literally iterates until analysis is clean, formatting is consistent, tests pass, and the output from `flutter run` confirms that pixels are being drawn. + +## In summary + +Without a doubt, these new AI tools are redefining how we identify as productive developers who enjoy the practice of programming. Equally without doubt, a robust, fast, uninterrupted loop will continue to be the centerpiece of that experience. + +Antigravity doesn’t replace the programmer; it removes the drudgery that stands between your idea and reality. The loop is getting faster, the ramp is already built, and the future of Flutter is more usable — and more joyful — than ever. \ No newline at end of file diff --git a/sites/www/content/blog/jaimes-build-context-prompt-engineering-as-infrastructure/images/1HNqQFQ2hdhAgjew1pIMqXA.webp b/sites/www/content/blog/jaimes-build-context-prompt-engineering-as-infrastructure/images/1HNqQFQ2hdhAgjew1pIMqXA.webp new file mode 100644 index 0000000000..3c8f2c4012 Binary files /dev/null and b/sites/www/content/blog/jaimes-build-context-prompt-engineering-as-infrastructure/images/1HNqQFQ2hdhAgjew1pIMqXA.webp differ diff --git a/sites/www/content/blog/jaimes-build-context-prompt-engineering-as-infrastructure/index.md b/sites/www/content/blog/jaimes-build-context-prompt-engineering-as-infrastructure/index.md new file mode 100644 index 0000000000..31e1a518ec --- /dev/null +++ b/sites/www/content/blog/jaimes-build-context-prompt-engineering-as-infrastructure/index.md @@ -0,0 +1,189 @@ +--- +title: "Jaime’s build context: Prompt engineering as infrastructure" +description: "Summary" +publishDate: 2026-01-23 +author: jwren +image: images/1HNqQFQ2hdhAgjew1pIMqXA.webp +layout: blog +--- + +<DashImage figure src="images/1HNqQFQ2hdhAgjew1pIMqXA.webp" /> + + +## Summary + +In the era of agentic AI, the bottleneck for scaling projects has shifted from code volume to code integrity. To move at the speed AI allows without introducing systemic friction, we must stop treating AI as a magic wand and start treating it as a high-performance engine that requires a perfectly calibrated track. + +* Close the gap: Engineering teams are splitting — traditionalists on one end and high-velocity agentic power users on the other. For production apps, your infrastructure must connect both, creating a shared space where traditional rigor and agentic speed don’t just coexist, but actively harden each other. + +* Lean into reproducible systems: An agent is only as good as its feedback loop. If your tests aren’t reproducible and your CI lacks a high-signal feedback loop, your agents are flying blind. + +* Prompts as infrastructure: Stop treating your best AI instructions as ephemeral chat one-offs that disappear when a session ends. Instead, move these automated workflow instructions into a version-controlled file, transforming your individual tribal knowledge into executable documentation that ensures every team member can trigger the same high-integrity results reliably across the project. + +* Shift to Skill-based architecture: Success means graduating your proven workflows into shared Skills. By moving from individual prompts to project-wide Skills, you provide your entire team with a standard, high-integrity playbook that agents can execute automatically. + +Recently, I’ve been reflecting on what it truly means to scale an open-source project for a new wave of contributors. We are moving into an era where agentic AI can generate code so rapidly that sheer volume is no longer the bottleneck; instead, the real challenge is ensuring this influx doesn’t compromise our codebases, or our shared values as teammates. Many teams are splitting into a dumbbell shape: traditional manual coding on one end and high-velocity agentic power-users on the other. + +For large apps, our infrastructure must serve as the handle that connects these two ends of the spectrum, creating a shared space where traditional rigor and agentic speed don’t just coexist, but actively harden each other. To scale effectively, we must lean into the basics by anchoring our workflows in the fundamentals of reproducible software development. To move at the speed these tools allow without drowning in technical debt or unreadable codebases, our environments must provide a definitive, high-signal feedback loop. + +In this agentic context, source code health is measured strictly by passing tests, deterministic coverage metrics, and clean analysis. If these fundamentals are missing, it’s like asking a high-performance engine to drive at breakneck speed through the dark. It is perfectly reasonable — and often responsible — to feel the need to push back against these new tools if the environment isn’t ready for them. + +The goal here isn’t to force a tool onto a project for the sake of speed; it’s to build the digital sensors that allow us to adopt that new velocity safely. More importantly, this transition is about bringing our teammates along for the ride. Infrastructure shouldn’t be a barrier that separates the “AI power users” from the rest of the team; it should be the common ground where we all feel confident. Being a good teammate in the agentic era means ensuring that though our repositories might move faster, our culture remains supportive. We build these systems so that everyone can move with confidence, ensuring that “agentic speed” is a win for the whole team, not just a new risk for an existing functioning software product. + +However, these digital sensors are only as reliable as the environment in which they live. This is why we must anchor our trust in our CI systems. In a truly reproducible workflow, the CI is the only version of reality that matters; it is the final arbiter for an agent that lacks human intuition. If a change doesn’t pass the automated gates, it effectively doesn’t exist. By treating the CI as a high-signal feedback loop, we remove the human bottleneck of manual verification and replace it with a reproducible gate. This absolute source of truth allows us to scale more responsibly — enabling agents to fail fast, iterate, and ultimately succeed without compromising the integrity of the project or the peace of mind of the team. + +## The ask + +A one-off prompting success is just a fluke. True scale comes from creating a flywheel: every time you or a teammate returns to the repository, the agent should be more capable than it was the last time because knowledge debt has been paid forward into a shared repository of prompts. You are building a shared institutional memory that isn’t trapped in a single developer’s head. + +To make this repeatable for any developer on your team, you need a library of prompt instructions that acts as executable documentation. While many teams rush to create global “config” and “rules” files, I find these often lead to inconsistent results — an agent might “forget” a rule or misapply it to the wrong context. As scientifically minded engineers, we should prefer to remove variables. By controlling the exact prompting for a specific task, you ensure the agent has exactly what it needs for that particular task within your repository. Doing so in an intentional manner in a project is what helps both individuals and teams build genuine confidence in these new tools. + +## Enter Agent Skills + +In late 2025, Anthropic introduced *Agent Skills* — an open standard designed to provide AI agents with specialized, modular expertise. Rather than just generic tools, Skills package instructions and resources together to ensure complex tasks are performed consistently. This is moving fast; as of early 2026, many tools are already adding support for this standard to enable cross-platform portability. + +* Flexibility: You can use Skills with many agents, including Antigravity, Cursor, Claude Code Gemini CLI and VS Code, because they are an [open standard](https://agentskills.io/home). + +* Shared knowledge: Skills can be easily shared with collaborators, or saved into a user folder for use across projects. + +* Repeatable workflows: Skills ensure that agents consistently perform complex, multi-step tasks by instructing them with clear procedural steps. + +* Additional resources: Skills can include scripts, templates, or other data alongside instructions. + +* Just-in-time access: When Skills are first loaded, only the metadata is loaded into the agent’s context. This means that the specific instructions are only added to the context when needed, which saves tokens. + +* Maintain code quality: The construction of these can be used as a set of tools to help contributors have the right context to put together higher quality PRs, tests, and documentation following your best practices. + +## Documentation for current major tools that support Skills + +See documentation for your tool to see where to add these files. + +* Antigravity — [https://antigravity.google/docs/skills](https://antigravity.google/docs/skills) + +* Cursor — [https://cursor.com/docs/context/skills](https://cursor.com/docs/context/skills) + +* Claude Code- [https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview) + +* Gemini CLI — [https://geminicli.com/docs/cli/skills/](https://geminicli.com/docs/cli/skills/) + +* VS Code — [https://code.visualstudio.com/docs/copilot/customization/agent-skills](https://code.visualstudio.com/docs/copilot/customization/agent-skills) + +## The anatomy of an effective Skill + +To ensure your Skills library remains a “Runbook for Robots” rather than just a list of suggestions, every prompt in this collection follows a strict set of best practices: + +* Known good state: Before an agent touches a line of code, it must verify that the existing tests pass and the analyzer is clean. If the foundation is cracked, the agent fixes the foundation before building on top of it. + +* Actionable verification: Anchor instructions in commands like `flutter test --coverage` or `dart fix --apply` to provide the agent with the deterministic feedback it needs to self-correct. + +* Finite scoping: To ensure that the agent maintains high context and focus, prompts are designed with clear boundaries — targeting specific functions, files, or coverage gaps. This ensures that the agent remains focused on a single goal, preventing scope creep and ensuring the output is easy to review. + +* The report & review loop: The agent’s job ends with a structured summary, not a commit. By requiring the agent to explain its changes and provide a suggested commit message without actually pushing code, we keep the human engineer in the role of the final decision maker. + +## Example Skill: Prepare current work for PR + +```markdown +--- +name: pr-prep +description: Prepare current work for PR +--- +## Prepare current work for PR: Verify Tests Passing and Cleanup + +**Objective:** Verify the current test suite status with `flutter test`, clean up any temporary modifications, and harden test coverage for active files. + +**Instructions:** +1. **Baseline:** + * Run `dart fix --apply` to apply automated fixes. + * Run `flutter analyze` to ensure no analysis issues. + * Run `flutter test` to establish the current passing state. + * Run `flutter test integration_test/app_test.dart` to verify integration integrity. +2. **Fix Failures:** If there are any test failures or analysis errors, investigate and resolve them. Prioritize fixing code over deleting tests. +3. **Cleanup:** Review any currently modified files (run `git status` or check the diff). Remove any: + * `print` / `debugPrint` statements. + * Unused imports. + * Commented-out code blocks. + * Temporary "hack" fixes that should be replaced with proper solutions. +4. **Verify & Expand:** + * For the files you touched or cleaned up, check if there are obvious edge cases missing from their unit tests. Add tests to cover these cases. + * Run `flutter analyze` again to ensure clean code. + * Run `flutter test` again to ensure cleanup didn't break anything. + * Repeat this step as necessary. +5. **Report & Review:** + * Summarize the cleanup status (e.g., "Tests passing, removed 3 debug prints"). + * **Action:** Ask the user to review the changes closely to ensure no intended code was accidentally removed. + * **Do not commit or push.** + * Provide a suggested Git commit message (e.g., "Prepare for PR: Fix tests and remove debug code"). +``` + + +## Example Skill: Single file test coverage + +```markdown +--- +name: single-file-test-coverage +description: Write a new test or modify an existing test +--- +## Single File Test Coverage Improvement + +**Objective:** Write a new single test file, or modify an existing file, to improve coverage for a specific target class. + +**Instructions:** +1. **Identify Target:** Choose a single source file (Dart) in `lib/` that has low or no test coverage and is suitable for unit testing (e.g., utility classes, logic helpers). +3. **Establish Baseline:** + * Run `flutter analyze` to ensure validity. + * Run `flutter test` to ensure the project is stable. + * Run `flutter test --coverage` and check `coverage/lcov.info`. +4. **Implement/Update Test:** Create a new test file in `test/` or update the existing one. Focus on: + * Edge cases (null inputs, empty strings, boundary values). + * Branch coverage (ensure if/else paths are exercised). + * Mocking dependencies where necessary (using `mockito` or `mocktail`). +5. **Verify & Iterate:** + * Run the tests to ensure they pass. + * Run `flutter analyze` to ensure no regressions. + * If coverage is still low, **iterate a few times**: analyze missed lines/branches and add targeted test cases. +5. **Report & Review:** + * Summarize what was fixed/covered and report coverage progress (e.g., `X% -> Y%` for `<filename>`). + * **Action:** Ask the user to review the new tests closely. + * **Do not commit or push.** + * Provide a suggested Git commit message (e.g., "Improve test coverage for [Class Name]"). +``` + + +## Example Skill: Migrate to modern Dart features + +```markdown +--- +name: migrate-to-modern-dart-features +description: Migrate to modern Dart features (Dart 3+) +--- +## Migrate to Modern Dart Features + +**Objective:** Optimize consistency and conciseness by migrating to modern Dart features (Dart 3+). + +**Candidates for Migration:** +* `if-else` chains -> `switch` expressions. +* Data classes with manual `==`/`hashCode` -> `Records` or `equatable` (or class modifiers). +* Null checks -> pattern matching. + +**Instructions:** +1. **Baseline:** Run `flutter test` and `flutter analyze`. +2. **Select Target:** Identify a *single* migration opportunity. +3. **Constraint:** Keep the change extremely small (**max 50 lines**). +4. **Migrate:** Refactor to use the new feature. +5. **Verify:** + * Run `flutter analyze`. + * Run `flutter test` to ensure no regressions. +6. **Report & Review:** + * Summarize the migration. + * **Action:** Ask the user to review the changes closely. + * **Test Location:** Explicitly state where in the app the user should go to manually test the change (e.g., "Click the bottom button after the app opens"). + * **Do not commit or push.** + * Provide a suggested Git commit message (e.g., "Refactor: Use switch expression in [Class Name]"). +``` + + +## In summary + +The long game of the agentic era isn’t about the volume of code you produce today; it’s about responsibly architecting the infrastructure and cultural expectations for the repositories of tomorrow. By building the handle of the dumbbell, ensuring that as our tools move faster, they remain anchored to our shared values and technical rigor. + +In the very near future, agents will move from reactive assistants to proactive contributors — checking in on your repository and resolving issues while you sleep. By hardening your high-signal feedback loops and documenting your workflows in a version-controlled suite of Skills, you are doing more than just saving time; you are training the agents of the future to respect your specific architecture, your vision, and your standards. Most importantly, you are ensuring that as the engine of development accelerates, your entire team has the digital sensors and shared knowledge to move forward with collective confidence and a stronger, more resilient engineering culture. \ No newline at end of file diff --git a/sites/www/content/blog/join-us-for-17daysofflutter/images/1cKk9QrcolzFfqB2ZWyUxSQ.webp b/sites/www/content/blog/join-us-for-17daysofflutter/images/1cKk9QrcolzFfqB2ZWyUxSQ.webp new file mode 100644 index 0000000000..52382f43a1 Binary files /dev/null and b/sites/www/content/blog/join-us-for-17daysofflutter/images/1cKk9QrcolzFfqB2ZWyUxSQ.webp differ diff --git a/sites/www/content/blog/join-us-for-17daysofflutter/index.md b/sites/www/content/blog/join-us-for-17daysofflutter/index.md new file mode 100644 index 0000000000..b5952437be --- /dev/null +++ b/sites/www/content/blog/join-us-for-17daysofflutter/index.md @@ -0,0 +1,39 @@ +--- +title: "Join us for #17DaysOfFlutter" +description: "Curated content leading up to the Flutter Forward event." +publishDate: 2023-01-05 +author: shamiramarshall +image: images/1cKk9QrcolzFfqB2ZWyUxSQ.webp +category: events +layout: blog +--- + +<DashImage figure src="images/1cKk9QrcolzFfqB2ZWyUxSQ.webp" alt="Banner for #17DaysOfFlutter" caption="Banner for #17DaysOfFlutter" /> + + +Happy 2023! + +Why not celebrate the new year by leveling up your Flutter skills? Starting January 9th, and leading up to our [Flutter Forward event](https://flutter.dev/events/flutter-forward) on January 25th, we’ll be hosting 17 Days of Flutter, a Flutter content marathon unveiling brand new content on Monday through Friday from our Flutter experts. Plus, a chance to apply what you learn and share your work with the Flutter community. Whether you’re just starting out with Flutter, or you’re a seasoned Flutter pro, we have new YouTube videos, codelabs, live events, and other curated content to help you along your learning journey. + +**#17DaysOfFlutter Schedule** + +The content leading up to Flutter Forward will look like this: + +**Week 1** + +The first week is all fun and games! Watch as Developer Relations Engineers [Khanh](https://twitter.com/khanhNwin) and [Eric](https://twitter.com/ericwindmill) build their first ever web game (using Flame) in [Learning to Fly Season 2](https://www.youtube.com/watch?v=CkcvVZZEsJE&list=PLjxrf2q8roU3X18pAQWLyCJaa79RpqWnn). Follow along as they try their hand at game development, or work at your own pace creating a game from scratch with our new Games codelab. Later in the week, learn about the [Flame](https://docs.flame-engine.org/1.4.0/index.html) game engine in a new episode of Widget of the Week. We’re providing several ways to get you building games in Flutter. + +**Week 2** + +In week two, we’re dropping another codelab and some blog posts about [Material 3](https://m3.material.io) and UX design that will reframe how you think about your Flutter app experience. Then check out The Boring Show — Live on January 17th, hosted by [Craig](https://twitter.com/craig_labenz) on the [Flutter YouTube channel](https://www.youtube.com/watch?v=NuX4b6qtU2U&list=PLjxrf2q8roU3ahJVrSgAnPjzkpGmL9Czl). To close out the second week of #17DaysOfFlutter, we’re demystifying [`dart fix`](https://dart.dev/tools/dart-fix) in a new Decoding Flutter video. + +**Community Game Submissions** + +Get involved in the Flutter community by sharing your own game! Starting January 14th, you’ll get a chance to participate in the games coding challenge. Power up Doodle Dash from Learning To Fly and the games codelab by adding a new feature or enemy. You can find out more details about how to share your game with the community on the [17DaysOfFlutter](https://flutter.dev/events/flutter-forward/#17-days-of-flutter) website. + +Be sure to follow [@FlutterDev](https://twitter.com/flutterdev) on Twitter, to get updates about #17DaysOfFlutter. + +<YoutubeEmbed id="hpgkrUPRBjc" title="17 days of Flutter!" fullwidth="true"/> + + +(Why 17DaysOfFlutter? Someone on the Flutter team decided to kick off Flutter Forward on a Wednesday, and we wanted at least two full weeks of content!) \ No newline at end of file diff --git a/sites/www/content/blog/join-us-for-30daysofflutter/images/1v7ytVK_8WbqA7Gc-Uol-wQ.webp b/sites/www/content/blog/join-us-for-30daysofflutter/images/1v7ytVK_8WbqA7Gc-Uol-wQ.webp new file mode 100644 index 0000000000..7f874d9a6a Binary files /dev/null and b/sites/www/content/blog/join-us-for-30daysofflutter/images/1v7ytVK_8WbqA7Gc-Uol-wQ.webp differ diff --git a/sites/www/content/blog/join-us-for-30daysofflutter/index.md b/sites/www/content/blog/join-us-for-30daysofflutter/index.md new file mode 100644 index 0000000000..e3ba77eb67 --- /dev/null +++ b/sites/www/content/blog/join-us-for-30daysofflutter/index.md @@ -0,0 +1,46 @@ +--- +title: "Join us for #30DaysOfFlutter" +description: "Happy New Year folks. It’s the perfect time of year to learn something new! Do you have an app idea you’ve been dreaming of over the…" +publishDate: 2021-01-26 +author: nikkitagandhi +image: images/1v7ytVK_8WbqA7Gc-Uol-wQ.webp +category: events +layout: blog +--- + +<DashImage figure src="images/1v7ytVK_8WbqA7Gc-Uol-wQ.webp" alt="Banner for 30 Days of Flutter" caption="Banner for 30 Days of Flutter" /> + + +Happy New Year folks. It’s the perfect time of year to learn something new! Do you have an app idea you’ve been dreaming of over the holidays? If so, we have just the opportunity for you! Starting February 1st, leading up to our big [event on March 3rd](https://events.flutter.dev/?utm_source=30DaysOfFlutter&utm_medium=email&utm_campaign=30Days_Engage), join us for **#30DaysOfFlutter** to kickstart your learning journey and meet Flutter experts in the community. Whether you are building your first Flutter app or looking to improve your Flutter skills, we have curated content, code labs, and demos! + +Flutter is Google’s open source UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase. It’s one of the fastest growing, most in-demand cross platform frameworks to learn and is used by freelance developers and large organizations around the world. Flutter uses the Dart language, so it will feel natural to many of you familiar with object-oriented languages. + +### **Jump in, the water’s fine!** + +Along with the curated content, we will also have four live AskMeAnything sessions (#AMAs), where you can meet members of Google’s Flutter team and community. You can also join us on the [FlutterDev Discord channel](https://discord.com/invite/N7Yshp4), where you can meet the other members of the community, ask and answer questions, and maybe make some new Flutter friends too! + +Does this sound exciting? Visit the [**30 Days of Flutter website**](http://goo.gle/30daysofflutter) to get more information and to register to join. Last date to register is 31st January 2021, 11:30pm PST. + +### **#30DaysOfFlutter Schedule** + +Your learning journey with Flutter for the month will look like this:: + +**Week 1** + +Receive curated content to your inbox. Meet other Flutter Devs on Discord. Attend Kick Off Webinar on February 1st. + +**Week 2** + +Receive more content. Start building your first Flutter app. Join the webinar and ask your questions. + +**Week 3** + +Work on your app and attend the 3rd webinar to ask your questions. + +**Week 4** + +Complete your project and learn how to share it with the Flutter community. + +Are you ready to learn one of the most in demand developer skills in the world? + +[*Sign up*](http://goo.gle/30daysofflutter) to be a part of the journey and be sure to follow [@FlutterDev](https://twitter.com/FlutterDev) on Twitter, to get updates about **#30DaysOfFlutter**. \ No newline at end of file diff --git a/sites/www/content/blog/keys-what-are-they-good-for/images/10NNY0KOBQGCWvdrWvorOQA.jpeg b/sites/www/content/blog/keys-what-are-they-good-for/images/10NNY0KOBQGCWvdrWvorOQA.jpeg new file mode 100644 index 0000000000..6a6fd7ff6f Binary files /dev/null and b/sites/www/content/blog/keys-what-are-they-good-for/images/10NNY0KOBQGCWvdrWvorOQA.jpeg differ diff --git a/sites/www/content/blog/keys-what-are-they-good-for/images/13XbdhaQ9_lPfILdViiipeQ.gif b/sites/www/content/blog/keys-what-are-they-good-for/images/13XbdhaQ9_lPfILdViiipeQ.gif new file mode 100644 index 0000000000..73589b1d74 Binary files /dev/null and b/sites/www/content/blog/keys-what-are-they-good-for/images/13XbdhaQ9_lPfILdViiipeQ.gif differ diff --git a/sites/www/content/blog/keys-what-are-they-good-for/images/17n-u4yexzRZDEtNvbrsG1g.gif b/sites/www/content/blog/keys-what-are-they-good-for/images/17n-u4yexzRZDEtNvbrsG1g.gif new file mode 100644 index 0000000000..97b571baad Binary files /dev/null and b/sites/www/content/blog/keys-what-are-they-good-for/images/17n-u4yexzRZDEtNvbrsG1g.gif differ diff --git a/sites/www/content/blog/keys-what-are-they-good-for/images/1AcBxC8IF_irZpFARt-Nqyw.gif b/sites/www/content/blog/keys-what-are-they-good-for/images/1AcBxC8IF_irZpFARt-Nqyw.gif new file mode 100644 index 0000000000..4ab7cc82dd Binary files /dev/null and b/sites/www/content/blog/keys-what-are-they-good-for/images/1AcBxC8IF_irZpFARt-Nqyw.gif differ diff --git a/sites/www/content/blog/keys-what-are-they-good-for/images/1FkCvw_LCfQ2x02wj7cmrpA.gif b/sites/www/content/blog/keys-what-are-they-good-for/images/1FkCvw_LCfQ2x02wj7cmrpA.gif new file mode 100644 index 0000000000..ff2dd6b3ab Binary files /dev/null and b/sites/www/content/blog/keys-what-are-they-good-for/images/1FkCvw_LCfQ2x02wj7cmrpA.gif differ diff --git a/sites/www/content/blog/keys-what-are-they-good-for/images/1JI1Ex87QRMTCJwBWmbNI5A.gif b/sites/www/content/blog/keys-what-are-they-good-for/images/1JI1Ex87QRMTCJwBWmbNI5A.gif new file mode 100644 index 0000000000..6d2cf79a9c Binary files /dev/null and b/sites/www/content/blog/keys-what-are-they-good-for/images/1JI1Ex87QRMTCJwBWmbNI5A.gif differ diff --git a/sites/www/content/blog/keys-what-are-they-good-for/images/1JIPjn-gM6OIG_TfPJvtuVA.gif b/sites/www/content/blog/keys-what-are-they-good-for/images/1JIPjn-gM6OIG_TfPJvtuVA.gif new file mode 100644 index 0000000000..db550709c2 Binary files /dev/null and b/sites/www/content/blog/keys-what-are-they-good-for/images/1JIPjn-gM6OIG_TfPJvtuVA.gif differ diff --git a/sites/www/content/blog/keys-what-are-they-good-for/images/1KgQeq1LDIPVuE2dwNzZbRQ.gif b/sites/www/content/blog/keys-what-are-they-good-for/images/1KgQeq1LDIPVuE2dwNzZbRQ.gif new file mode 100644 index 0000000000..52527365c2 Binary files /dev/null and b/sites/www/content/blog/keys-what-are-they-good-for/images/1KgQeq1LDIPVuE2dwNzZbRQ.gif differ diff --git a/sites/www/content/blog/keys-what-are-they-good-for/images/1T7TBQx9DhaQ16gbX68XxVw.gif b/sites/www/content/blog/keys-what-are-they-good-for/images/1T7TBQx9DhaQ16gbX68XxVw.gif new file mode 100644 index 0000000000..381fe0a5e3 Binary files /dev/null and b/sites/www/content/blog/keys-what-are-they-good-for/images/1T7TBQx9DhaQ16gbX68XxVw.gif differ diff --git a/sites/www/content/blog/keys-what-are-they-good-for/images/1edgczyvaQRgGRy8yhht0QQ.gif b/sites/www/content/blog/keys-what-are-they-good-for/images/1edgczyvaQRgGRy8yhht0QQ.gif new file mode 100644 index 0000000000..2bb36e6d4d Binary files /dev/null and b/sites/www/content/blog/keys-what-are-they-good-for/images/1edgczyvaQRgGRy8yhht0QQ.gif differ diff --git a/sites/www/content/blog/keys-what-are-they-good-for/images/1noTkKudlGuaAkiGaubEcNA.gif b/sites/www/content/blog/keys-what-are-they-good-for/images/1noTkKudlGuaAkiGaubEcNA.gif new file mode 100644 index 0000000000..fc747e0abd Binary files /dev/null and b/sites/www/content/blog/keys-what-are-they-good-for/images/1noTkKudlGuaAkiGaubEcNA.gif differ diff --git a/sites/www/content/blog/keys-what-are-they-good-for/images/1sHDIVXBu9RpJYN9Zdn8iBw.gif b/sites/www/content/blog/keys-what-are-they-good-for/images/1sHDIVXBu9RpJYN9Zdn8iBw.gif new file mode 100644 index 0000000000..9e195ed3af Binary files /dev/null and b/sites/www/content/blog/keys-what-are-they-good-for/images/1sHDIVXBu9RpJYN9Zdn8iBw.gif differ diff --git a/sites/www/content/blog/keys-what-are-they-good-for/images/1uC-SRZpRkOZCEr_rGisF9g.gif b/sites/www/content/blog/keys-what-are-they-good-for/images/1uC-SRZpRkOZCEr_rGisF9g.gif new file mode 100644 index 0000000000..7d3ad8655c Binary files /dev/null and b/sites/www/content/blog/keys-what-are-they-good-for/images/1uC-SRZpRkOZCEr_rGisF9g.gif differ diff --git a/sites/www/content/blog/keys-what-are-they-good-for/images/1vD86ZINBC-1Ctx9kudEGaw.gif b/sites/www/content/blog/keys-what-are-they-good-for/images/1vD86ZINBC-1Ctx9kudEGaw.gif new file mode 100644 index 0000000000..7ce9d126f9 Binary files /dev/null and b/sites/www/content/blog/keys-what-are-they-good-for/images/1vD86ZINBC-1Ctx9kudEGaw.gif differ diff --git a/sites/www/content/blog/keys-what-are-they-good-for/images/1vZV_QjG1GEg7nJILMbhEkA.webp b/sites/www/content/blog/keys-what-are-they-good-for/images/1vZV_QjG1GEg7nJILMbhEkA.webp new file mode 100644 index 0000000000..2603f0e7cb Binary files /dev/null and b/sites/www/content/blog/keys-what-are-they-good-for/images/1vZV_QjG1GEg7nJILMbhEkA.webp differ diff --git a/sites/www/content/blog/keys-what-are-they-good-for/images/1wHJZnNPhMkePFEw1ihrbEA.gif b/sites/www/content/blog/keys-what-are-they-good-for/images/1wHJZnNPhMkePFEw1ihrbEA.gif new file mode 100644 index 0000000000..4bee5c56e9 Binary files /dev/null and b/sites/www/content/blog/keys-what-are-they-good-for/images/1wHJZnNPhMkePFEw1ihrbEA.gif differ diff --git a/sites/www/content/blog/keys-what-are-they-good-for/index.md b/sites/www/content/blog/keys-what-are-they-good-for/index.md new file mode 100644 index 0000000000..2d1c8b90a3 --- /dev/null +++ b/sites/www/content/blog/keys-what-are-they-good-for/index.md @@ -0,0 +1,398 @@ +--- +title: "Keys! What are they good for?" +description: "The key parameter can be found on basically every widget constructor, but their use is less common. Keys preserve state when widgets move…" +publishDate: 2019-03-29 +author: efortuna +image: images/1sHDIVXBu9RpJYN9Zdn8iBw.gif +category: tutorial +layout: blog +--- + +The key parameter can be found on basically every widget constructor, but their use is less common. Keys preserve state when widgets move around in your widget tree. In practice, this means they can be useful to preserve the user’s scroll location or keep state when modifying a collection. + +The following article is adapted from the following video: + +<YoutubeEmbed id="kn0EOS-ZiIc" title="When to Use Keys - Flutter Widgets 101 Ep. 4" fullwidth="true"/> + + +If you prefer to listen/watch rather than read, that video should cover all of the same content. + +## The Inside Scoop on Keys + +Most of the time… you don’t need keys! Generally, there’s no harm adding them, but it’s also unnecessary and just takes up unnecessary space, just like the new keyword, or declaring types on both the right side and left side of a new variable (I’m looking at you, `Map<Foo, Bar> aMap = Map<Foo, Bar>()`). But, if you find yourself adding, removing, or reordering a collection of widgets of the same type that *hold some state*, using keys is likely in your future! +> # If you find yourself adding, removing, or reordering a collection of widgets of the same type that *hold some state*, using keys is likely in your future. + +To illustrate why you need keys when modifying a collection of widgets, I wrote an extremely simple app with two randomly colored widgets that swap places when you tap a button: + +<DashImage figure src="images/1edgczyvaQRgGRy8yhht0QQ.gif" /> + + +In the stateless version of the app, I have two stateless Tiles, each with a randomly generated color, in a `Row` and a `StatefulWidget` called `PositionedTiles` to store the position of these tiles. When I tap the `FloatingActionButton` down at the bottom it properly swaps their position in the list: + +```dart +void main() => runApp(new MaterialApp(home: PositionedTiles())); + +class PositionedTiles extends StatefulWidget { + @override + State<StatefulWidget> createState() => PositionedTilesState(); +} + +class PositionedTilesState extends State<PositionedTiles> { + List<Widget> tiles = [ + StatelessColorfulTile(), + StatelessColorfulTile(), + ]; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Row(children: tiles), + floatingActionButton: FloatingActionButton( + child: Icon(Icons.sentiment_very_satisfied), onPressed: swapTiles), + ); + } + + swapTiles() { + setState(() { + tiles.insert(1, tiles.removeAt(0)); + }); + } +} + +class StatelessColorfulTile extends StatelessWidget { + Color myColor = UniqueColorGenerator.getColor(); + @override + Widget build(BuildContext context) { + return Container( + color: myColor, child: Padding(padding: EdgeInsets.all(70.0))); + } +} +``` + +But if we make those `ColorfulTiles` *stateful* instead of stateless and store the color in the state, when I press the button it looks like nothing is happening. + +<DashImage figure src="images/1T7TBQx9DhaQ16gbX68XxVw.gif" /> + + +```dart +List<Widget> tiles = [ + StatefulColorfulTile(), + StatefulColorfulTile(), +]; + +... +class StatefulColorfulTile extends StatefulWidget { + @override + ColorfulTileState createState() => ColorfulTileState(); +} + +class ColorfulTileState extends State<ColorfulTile> { + Color myColor; + + @override + void initState() { + super.initState(); + myColor = UniqueColorGenerator.getColor(); + } + + @override + Widget build(BuildContext context) { + return Container( + color: myColor, + child: Padding( + padding: EdgeInsets.all(70.0), + )); + } +} + +``` + +Just a reminder, the code shown above is *buggy*, in that it does not show the colors swapping when the user presses the “swap” button. The fix for this is to add a key parameter to the stateful widgets, and then the widgets swap places like we want: + +<DashImage figure src="images/13XbdhaQ9_lPfILdViiipeQ.gif" /> + + +```dart +List<Widget> tiles = [ + StatefulColorfulTile(key: UniqueKey()), // Keys added here + StatefulColorfulTile(key: UniqueKey()), +]; + +... +class StatefulColorfulTile extends StatefulWidget { + StatefulColorfulTile({Key key}) : super(key: key); // NEW CONSTRUCTOR + + @override + ColorfulTileState createState() => ColorfulTileState(); +} + +class ColorfulTileState extends State<ColorfulTile> { + Color myColor; + + @override + void initState() { + super.initState(); + myColor = UniqueColorGenerator.getColor(); + } + + @override + Widget build(BuildContext context) { + return Container( + color: myColor, + child: Padding( + padding: EdgeInsets.all(70.0), + )); + } +} +``` + +But, this is only necessary if you have *stateful* widgets in the subtree that you are modifying. If the entire widget subtree in your collection is *stateless*, keys *aren’t* needed. + +That’s it! That’s technically all you need to know to use keys in Flutter. But if you’d like to understand the underlying why of it all…. + +## The Nitty Gritty of Why Keys are Sometimes Needed + +You’re still here, eh? Well, then gather near, to learn the true nature of Widget and Element trees to become a Flutter Sorcerer! Mwahahaha! Ha ha! Ha HA! Ahem, excuse me. + +As you may know, under the covers, for every widget, Flutter builds a corresponding `Element`. Just like constructing a Widget tree, Flutter also builds an `Element` tree. The `ElementTree` is extremely simple, only holding information about the *type* of each widget and a reference to children elements. You can think of the `ElementTree` like a skeleton of your Flutter app. It shows the structure of your app, but all the additional information can be looked up via reference to the original widget. + +The `Row` widget in the example above essentially holds a set of ordered slots for each of its children. When we swap the order of the Tile widgets in the `Row`, Flutter walks the `ElementTree` to see if the skeletal structure is the same. + +<DashImage figure src="images/1sHDIVXBu9RpJYN9Zdn8iBw.gif" /> + + +It starts with the `RowElement`, and then moves to its children. The `ElementTree` checks that the new widget is the same *type* and *key* as the old one, and if so, it updates its reference to the new widget. In the stateless version, the widgets don’t have keys, so Flutter just checks the type. (If this seems like a lot of information at once, watch the animated diagram above.) + +The underlying `Element` tree structure for *stateful* widgets looks a little different. There are widgets and elements like before, but also there are a pair of state objects with them, and the color information is being stored there, not in the widgets themselves. + +<DashImage figure src="images/1noTkKudlGuaAkiGaubEcNA.gif" /> + + +In the stateful `Tile` case without keys, when I swap the order of the two widgets, Flutter walks the `ElementTree`, checks the type of the `RowWidget`, and updates the reference. Then `TileElement` checks that the corresponding widget is the same *type* (`TileWidget`) and it is, so it updates the reference. The same thing happens for the second child. Because Flutter uses the `ElementTree` and its corresponding *state* to determine what to actually display on your device, from our perspective, it looks like your widgets didn’t properly swap! + +<DashImage figure src="images/17n-u4yexzRZDEtNvbrsG1g.gif" /> + + +In the fixed version with the stateful `Tiles`, I added key properties to the widgets. Now if we swap the widgets The `Row` widgets match like before, but the key of the Tile Element doesn’t match the key of the corresponding Tile Widget. This causes Flutter to deactivate those elements and remove the references to the Tile Elements in the Element Tree, starting with the first one that doesn’t match. + +<DashImage figure src="images/1AcBxC8IF_irZpFARt-Nqyw.gif" /> + + +Then Flutter looks through to non-matched children of the `Row` for an element with the correct corresponding key. It finds a match, and updates its reference to the corresponding widget. Flutter then does the same thing for the second child. Now Flutter will display what we expect, with the widgets swapping places and updating their color when I press the button. + +So in summary, keys are useful if you’re modifying the *order* or *number* of stateful widgets in a collection. For the sake of illustration, I stored the color as state in this example. Often though, state is much more subtle. Playing animations, displaying data that the user has entered, and scroll location all involve state. + +## Where do I put ‘em? + +Short answer: if you need to add keys to your app, you should add them at the ***top*** of the widget subtree with the state you need to preserve. + +A common mistake I’ve seen is people thinking they only need to put a key on the first stateful widget, but *there be dragons.* Don’t believe me? To show what sort of trouble we can get into, I wrapped my colorfulTile widgets with padding widgets, but I left the keys on the tiles. + +```dart +void main() => runApp(new MaterialApp(home: PositionedTiles())); + +class PositionedTiles extends StatefulWidget { + @override + State<StatefulWidget> createState() => PositionedTilesState(); +} + +class PositionedTilesState extends State<PositionedTiles> { + // Stateful tiles now wrapped in padding (a stateless widget) to increase height + // of widget tree and show why keys are needed at the Padding level. + List<Widget> tiles = [ + Padding( + padding: const EdgeInsets.all(8.0), + child: StatefulColorfulTile(key: UniqueKey()), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: StatefulColorfulTile(key: UniqueKey()), + ), + ]; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Row(children: tiles), + floatingActionButton: FloatingActionButton( + child: Icon(Icons.sentiment_very_satisfied), onPressed: swapTiles), + ); + } + + swapTiles() { + setState(() { + tiles.insert(1, tiles.removeAt(0)); + }); + } +} + +class StatefulColorfulTile extends StatefulWidget { + StatefulColorfulTile({Key key}) : super(key: key); + + @override + ColorfulTileState createState() => ColorfulTileState(); +} + +class ColorfulTileState extends State<ColorfulTile> { + Color myColor; + + @override + void initState() { + super.initState(); + myColor = UniqueColorGenerator.getColor(); + } + + @override + Widget build(BuildContext context) { + return Container( + color: myColor, + child: Padding( + padding: EdgeInsets.all(70.0), + )); + } +} +``` + +Now when I click the button the Tiles change to completely different random colors! + +<DashImage figure src="images/1uC-SRZpRkOZCEr_rGisF9g.gif" /> + + +Here’s what the WidgetTree and ElementTree look like with the padding widgets added: + +<DashImage figure src="images/10NNY0KOBQGCWvdrWvorOQA.jpeg" /> + + +When we swap the positions of the children, Flutter’s element-to-widget-matching algorithm looks at one level in the tree at a time. The diagram greys out the children’s children in the diagram so we can focus on one level at a time. At that first level of children with the Padding elements, everything matches up correctly. + +<DashImage figure src="images/1vD86ZINBC-1Ctx9kudEGaw.gif" /> + + +At the second level, Flutter notices that the key of the Tile Element doesn’t match the key of the widget, so it deactivates that Tile Element, dropping those connections. The keys we’re using in this example are `LocalKeys`. That means that when matching up widget to elements, Flutter only looks for key matches within a particular level in the tree. + +Since it can’t find a tile element at that level with that key value, it creates a new one, and initializes a new state, in this case, making the widget orange! + +<DashImage figure src="images/1JI1Ex87QRMTCJwBWmbNI5A.gif" /> + + +If we add keys at the level of the padding widgets: + +```dart +void main() => runApp(new MaterialApp(home: PositionedTiles())); + +class PositionedTiles extends StatefulWidget { + @override + State<StatefulWidget> createState() => PositionedTilesState(); +} + +class PositionedTilesState extends State<PositionedTiles> { + List<Widget> tiles = [ + Padding( + // Place the keys at the *top* of the tree of the items in the collection. + key: UniqueKey(), + padding: const EdgeInsets.all(8.0), + child: StatefulColorfulTile(), + ), + Padding( + key: UniqueKey(), + padding: const EdgeInsets.all(8.0), + child: StatefulColorfulTile(), + ), + ]; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Row(children: tiles), + floatingActionButton: FloatingActionButton( + child: Icon(Icons.sentiment_very_satisfied), onPressed: swapTiles), + ); + } + + swapTiles() { + setState(() { + tiles.insert(1, tiles.removeAt(0)); + }); + } +} + +class StatefulColorfulTile extends StatefulWidget { + StatefulColorfulTile({Key key}) : super(key: key); + + @override + ColorfulTileState createState() => ColorfulTileState(); +} + +class ColorfulTileState extends State<ColorfulTile> { + Color myColor; + + @override + void initState() { + super.initState(); + myColor = UniqueColorGenerator.getColor(); + } + + @override + Widget build(BuildContext context) { + return Container( + color: myColor, + child: Padding( + padding: EdgeInsets.all(70.0), + )); + } +} +``` + +Flutter notices the problem and updates the connections correctly, just like it did in our previous example. Order is restored in the universe. + +<DashImage figure src="images/1FkCvw_LCfQ2x02wj7cmrpA.gif" /> + + +## What kind of Key should I use? + +The fine purveyors of Flutter APIs have given us a variety of Key classes to choose from. The type of key you should use depends on what the distinguishing characteristic is for the items needing keys. Take a look at the information that you’re storing in those widgets. Here I’ll talk about four different types of keys: **`ValueKey`**, **`ObjectKey`**, **`UniqueKey`**, and**`UniqueKey`**. + +Consider the following To-do list app¹, where you can reorder the items in your TODO list based on priority and then remove them when you’re done. + +<DashImage figure src="images/1wHJZnNPhMkePFEw1ihrbEA.gif" /> + + +In this scenario, you might expect the text of a To-do item to be constant and unique. If that is the case, it is probably a good candidate for a **`ValueKey`**, where the text is the “value”. + +``` +return TodoItem( + key: ValueKey(todo.task), + todo: todo, + onDismissed: (direction) => _removeTodo(context, todo), +); +``` + + +In a different scenario, perhaps you had an address book app that listed information about each user. In this case each child widget stores a more complex combination of data. Any of the individual fields, like a first name or birthday might be the same as another entry, but the combination is unique. In this scenario, an **`ObjectKey`** is probably most appropriate. + +<DashImage figure src="images/1vZV_QjG1GEg7nJILMbhEkA.webp" /> + + +If you have multiple widgets in your collection with the same value or if you want to really ensure each widget is distinct from *all* others, you can use the **`UniqueKey`**. I used a `UniqueKey` in the example color-switching app because we didn’t have any other constant data that we’re storing in our tiles, and we don’t know what the color will be when we construct the widget. Be careful with `UniqueKey` though! If you construct a new`UniqueKey` inside a `build` method, the widget using that key will get a different, *unique* key every time you the build method re-executes. This will eliminate any benefits of using keys! + +Similarly, one thing you definitely *don’t* want to use is a random number for your key. Every time a widget gets built, a new random number will be generated and you’ll lose consistency between frames. Then you might as well not have used keys in the first place! + +**`PageStorageKey`**s are specialized keys that store a user’s scroll location so that the app can preserve it for later. + +<DashImage figure src="images/1KgQeq1LDIPVuE2dwNzZbRQ.gif" /> + + +**`GlobalKeys`** have two uses: they allow widgets to change parents anywhere in your app without losing state, or they can be used to access information about another widget in a completely different part of the widget tree. An example of the first scenario might if you wanted to show the same widget on two different screens, but holding all the same state, you’d want to use a `GlobalKey`. In the second scenario, maybe you want to validate a password, but don’t want to share that state information with other widgets in the tree. `GlobalKeys` can also be useful for testing, by using a key to access a particular widget and query information about its state. + +<DashImage figure src="images/1JIPjn-gM6OIG_TfPJvtuVA.gif" /> + + +Often (but not always!), `GlobalKeys` are a little like global variables. There is usually a better way to look up that state, using `InheritedWidgets`, or something like Redux or the BLoC pattern. + +## Quick Recap + +In summary, use Keys when you want to preserve state across widget trees. This most commonly occurs when you’re modifying a collection of widgets of the same type, like in a list. Put the key at the *top* of the widget tree you want preserved, and choose the key type you use based on the data you are storing in the widget. + +Congratulations, you are now well on your way to becoming a Flutter Sorcerer! Oh, did I say sorcerer? I meant *sourcerer*, as in someone who writes app source code…which is almost just as good. …Almost. ⚡ + +[1] Code for the To-do app inspired by [https://github.com/brianegan/flutter_architecture_samples/tree/master/example/vanilla](https://github.com/brianegan/flutter_architecture_samples/tree/master/example/vanilla) \ No newline at end of file diff --git a/sites/www/content/blog/landing-flutter-3-22-and-dart-3-4-at-google-i-o-2024/images/0F19xflZ4BfoCekFQ.webp b/sites/www/content/blog/landing-flutter-3-22-and-dart-3-4-at-google-i-o-2024/images/0F19xflZ4BfoCekFQ.webp new file mode 100644 index 0000000000..ad0a46c50a Binary files /dev/null and b/sites/www/content/blog/landing-flutter-3-22-and-dart-3-4-at-google-i-o-2024/images/0F19xflZ4BfoCekFQ.webp differ diff --git a/sites/www/content/blog/landing-flutter-3-22-and-dart-3-4-at-google-i-o-2024/images/0cF6UE3y05YTyMBQn.webp b/sites/www/content/blog/landing-flutter-3-22-and-dart-3-4-at-google-i-o-2024/images/0cF6UE3y05YTyMBQn.webp new file mode 100644 index 0000000000..86f6f6e3a6 Binary files /dev/null and b/sites/www/content/blog/landing-flutter-3-22-and-dart-3-4-at-google-i-o-2024/images/0cF6UE3y05YTyMBQn.webp differ diff --git a/sites/www/content/blog/landing-flutter-3-22-and-dart-3-4-at-google-i-o-2024/images/0ebpdPSPlak3EwoAU.webp b/sites/www/content/blog/landing-flutter-3-22-and-dart-3-4-at-google-i-o-2024/images/0ebpdPSPlak3EwoAU.webp new file mode 100644 index 0000000000..407842d9ae Binary files /dev/null and b/sites/www/content/blog/landing-flutter-3-22-and-dart-3-4-at-google-i-o-2024/images/0ebpdPSPlak3EwoAU.webp differ diff --git a/sites/www/content/blog/landing-flutter-3-22-and-dart-3-4-at-google-i-o-2024/images/0rWeEWxzBvcmOFNM4.webp b/sites/www/content/blog/landing-flutter-3-22-and-dart-3-4-at-google-i-o-2024/images/0rWeEWxzBvcmOFNM4.webp new file mode 100644 index 0000000000..88fcee9f63 Binary files /dev/null and b/sites/www/content/blog/landing-flutter-3-22-and-dart-3-4-at-google-i-o-2024/images/0rWeEWxzBvcmOFNM4.webp differ diff --git a/sites/www/content/blog/landing-flutter-3-22-and-dart-3-4-at-google-i-o-2024/images/1Mq-Uj8c8l7rrpPxYQ6Gemw.gif b/sites/www/content/blog/landing-flutter-3-22-and-dart-3-4-at-google-i-o-2024/images/1Mq-Uj8c8l7rrpPxYQ6Gemw.gif new file mode 100644 index 0000000000..c61f36f7c1 Binary files /dev/null and b/sites/www/content/blog/landing-flutter-3-22-and-dart-3-4-at-google-i-o-2024/images/1Mq-Uj8c8l7rrpPxYQ6Gemw.gif differ diff --git a/sites/www/content/blog/landing-flutter-3-22-and-dart-3-4-at-google-i-o-2024/index.md b/sites/www/content/blog/landing-flutter-3-22-and-dart-3-4-at-google-i-o-2024/index.md new file mode 100644 index 0000000000..9c414a9185 --- /dev/null +++ b/sites/www/content/blog/landing-flutter-3-22-and-dart-3-4-at-google-i-o-2024/index.md @@ -0,0 +1,128 @@ +--- +title: "Landing Flutter 3.22 and Dart 3.4 at Google I/O 2024" +description: "Major milestones for Flutter web apps, graphics performance upgrades, productivity experiments, and more" +publishDate: 2024-05-14 +author: mit-mit +image: images/1Mq-Uj8c8l7rrpPxYQ6Gemw.gif +category: events +layout: blog +--- + +It’s been a particularly busy last few months in Dart & Flutter air traffic control, but we’re happy to announce that Flutter 3.22 and Dart 3.4 have landed and are available today, just in time for this year’s [Google I/O](https://io.google/2024/)! + +We remain committed to providing a strong language and framework pairing, enabling you to build beautiful, rich, and fast apps from a single, shared code base — so you can deliver apps to users on mobile, web, and desktop — without having to fragment your product roadmap. + +<DashImage figure src="images/1Mq-Uj8c8l7rrpPxYQ6Gemw.gif" /> + + +Flutter 3.22 and Dart 3.4 offer performance improvements and platform-specific refinements that bring us closer to that vision. We’re particularly excited to share more about our journey with Wasm, but you’ll find so much more, including an improved Impeller rendering engine, smoother visuals and reduced CPU usage on iOS, enhanced platform navigation with Android’s predictive back gesture, expanded monetization options with the Google Mobile Ads SDK, and a new powerful deep link validator in DevTools. Dart developers will enjoy streamlined API migration with `dart fix` directly in the IDE and new DevTools capabilities for advanced profiling. Plus, you can now preview the Vertex AI for Firebase Dart SDK, integrating AI-powered features with robust security measures. + +Today, we’ll also spotlight top companies using Flutter to increase productivity and build performant experiences. We’ll highlight just a few particularly exciting features that move us closer to our goal to deliver native-like performance across platforms, and include selected updates on our investments in productivity, developer experience, and games. + +If you want more details on everything included in the releases, check out the dedicated [Flutter](https://medium.com/flutter/fbde6c164fe3) and [Dart](https://medium.com/dartlang/dart-3-4-bd8d23b4462a) posts. It’s time to Dash! + +## Flutter in action + +Flutter developers are busy launching large, new apps into the app stores. Let’s look at a few. + +<DashImage figure src="images/0cF6UE3y05YTyMBQn.webp" /> + + +### Helping large, enterprise-scale applications deliver on mobile and the web + +* In the United Kingdom, financial institution [Virgin Money](https://uk.virginmoney.com/) is [using Flutter](http://flutter.dev/showcase/virgin-money) across their suite of mobile banking and credit card apps to unify app development processes, speed up change, and enable an industry-leading user experience. + +* US insurance company [GEICO](https://www.geico.com/about/corporate/at-a-glance/) recently [shared](https://www.geico.com/techblog/flutter-as-the-multi-channel-ux-framework/) that Flutter helped them improve branded user experiences across iOS, Android, and the web, reduce the size of their codebase, and increase development efficiency, all at scale. + +* [Universal Studios Destinations & Experiences](https://corporate.universaldestinationsandexperiences.com/) released new Flutter mobile apps for their Hollywood, Osaka, and Orlando parks. Watch the video below to learn why they chose Flutter and to see how it’s performing so far. + +<YoutubeEmbed id="JTk2Exr7FO4" title="How Universal Destinations & Experiences build next generation experiences with #Flutter" fullwidth="true"/> + + +### Going beyond mobile and the web + +* The [Canonical](https://canonical.com/) team has been working with Flutter [since 2021](https://ubuntu.com/blog/flutter-and-ubuntu-so-far) to enable support for the Flutter [ecosystem](https://pub.dev/publishers/canonical.com/packages) in Ubuntu. Over the past year the Canonical team has rebuilt the Ubuntu Installer from the ground up with Flutter. + +* [LG](https://www.lg.com/us) has chosen Flutter to enhance their smart TV operating system, webOS. Flutter’s performance, productivity, and strong ecosystem allow LG to rapidly develop and deploy webOS system apps that run smoothly. By 2025, Flutter will power system apps on tens of millions of LG TVs worldwide. + +We’re inspired by these success stories, and are committed to making Flutter even better. Let’s dive into the latest product updates to show what we’re doing to enable you to build even more amazing apps, games, and experiences. + +## WebAssembly: Chasing native performance on the web + +Today we’re announcing support in our stable release for [compiling](https://docs.flutter.dev/platform-integration/web/wasm) Flutter web apps to WebAssembly (Wasm). This is an exciting new instruction format for web browsers, which provides a portable, platform neutral, binary code format. + +Our support for Wasm has been a deep, multi-year investment. First we partnered with the Chrome team on defining support in WebAssembly for high-level, managed languages like Dart, which commonly use garbage collection. This resulted in the [WasmGC](https://developer.chrome.com/blog/wasmgc/) [proposal](https://github.com/WebAssembly/gc/blob/main/proposals/gc/Overview.md), which is now a full and finalized standard, with runtime implementations available in Chrome (Chromium 119 and later) and Firefox (120 and later), and with other browser vendors expected to follow. Next, we added a brand new Dart compiler backend to generate WasmGC code, and the Dart & Flutter teams collaborated to run both the compiled app code and the Flutter rendering engine as Wasm modules with efficient Wasm to Wasm interop. + +So what’s the net result? We’re seeing much improved performance, approaching what we have on mobile and desktop devices running machine code. In our internal benchmarks (in Chrome on an M1 MacBook), the frame rendering time of [Wonderous](https://flutter.gskinner.com/wonderous/) improved by 2x in the general case and by 3x for the 99% worst case performance. Improved rendering performance is critical in demanding apps that feature animations and rich transitions, where exceeding the frame budget (the time allocated to render the next frame) causes very visible jank. Wasm has the potential to eliminate that, as illustrated by the juxtaposition below of the Wonderous app running with our traditional JS compilation compared to Wasm compilation. + +<DashImage figure src="images/0ebpdPSPlak3EwoAU.webp" alt="*Comparing the rendering speed of Javascript vs Wasm for the [Wonderous](https://flutter.gskinner.com/wonderous/) demo application.*" caption="*Comparing the rendering speed of Javascript vs Wasm for the [Wonderous](https://flutter.gskinner.com/wonderous/) demo application.*" /> + + +Wasm compilation for Flutter web apps is available in stable today. To get started, check out our [Dart Wasm documentation](https://dart.dev/web/wasm) and [Flutter Wasm documentation](https://docs.flutter.dev/platform-integration/web/wasm). + +## Dart macros: raising the development abstraction level + +We’re committed to providing a best-in-class developer experience. That means tackling long standing pain-points for Dart developers, like serializing JSON data. + +It’s a prevalent pattern that’s equal parts trivial and tedious. Current solutions mean either slogging through encoding and decoding boilerplate manually, or layering on extra tooling in the form of code generation solutions like the [JsonSerializable](https://docs.flutter.dev/data-and-backend/serialization/json#serializing-json-using-code-generation-libraries) package. + +Today, we’re announcing a preview of a better option for JSON: the [JsonCodable](https://dart.dev/go/json-codable) macro. + +Macros are code that creates more code. They’re like code generation, except the macro system is *built into Dart* and happens *in real-time* as you are editing and running the code. It’s an integrated experience without delays, fully supporting our existing developer workflows such as hot reload, as illustrated by this screencast: + +<DashImage figure src="images/0rWeEWxzBvcmOFNM4.webp" alt="*Screencast showing the experience of using a macro: Initially no toJson code completion exists, but after adding @JsonCodable to the class, the toJson code completion shows up immediately.*" caption="*Screencast showing the experience of using a macro: Initially no toJson code completion exists, but after adding @JsonCodable to the class, the toJson code completion shows up immediately.*" /> + + +We’re excited to see macros solve all kinds of problems for our developers. Take, for example, data classes, the [highest voted](https://github.com/dart-lang/language/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc) Dart language feature. Beyond specific applications, our eventual goal is to have a macro system in Dart that allows users to create their own macros and raise the abstraction level of Dart programming. + +Designing and implementing such a powerful macro system is a large task, so there is currently no date set for a stable release — for more details check out the [Dart 3.4 post](https://medium.com/dartlang/dart-3-4-bd8d23b4462a). In the meantime, try out a preview of the `JsonCodable` macro today. For more information, check out[ macros documentation](http://dart.dev/go/json-codable). + +## New resources for Flutter game development + +<DashImage figure src="images/0F19xflZ4BfoCekFQ.webp" /> + + +We’re seeing promising results from our early investment in games, including success stories from industry leaders like [Etermax](http://flutter.dev/showcase/etermax) and [Supercell](http://flutter.dev/showcase/supercell), who are each leveraging Flutter’s power and flexibility to efficiently deliver delightful user experiences and expand their reach. + +Today, we’re excited to continue to build on that momentum with these new resources for Flutter game developers: + +* The [SoLoud audio engine](https://pub.dev/packages/flutter_soloud) is now available: We’ve collaborated with the Flutter community (@[Marco Bavagnoli](https://github.com/alnitak)) to create a free and portable audio engine that delivers low-latency, high-performance sound, essential for many games. We also created a new [sound and music codelab](https://codelabs.developers.google.com/codelabs/flutter-codelab-soloud#0) that uses SoLoud. + +* Learn how to [generate expansive crossword-style grids](https://codelabs.developers.google.com/codelabs/flutter-word-puzzle#0) of interlocking words without compromising the user experience, and [craft game mechanics in a Flutter and Flame game using a 2D physics simulation](https://codelabs.developers.google.com/codelabs/flutter-flame-forge2d#0) along the lines of Box2D called [Forge2D](https://pub.dev/packages/forge2d). + +* A new guide for [optimizing Flutter web-based game loading speed](https://medium.com/flutter/best-practices-for-optimizing-flutter-web-loading-speed-7cc0df14ce5c): Long load times are a major deterrent in web-based games. Follow this guide (authored by [Cheng Lin](https://twitter.com/chenglinlim)) to optimize your loading speed so that players don’t abandon your game because it loads slowly. + +## Empowering sustainable game development + +In January 2024, inspired by the [story](https://www.youtube.com/watch?v=qgOlg173gcI&pp=ygUWR2xvYmFsIENpdGl6ZW4gRmx1dHRlcg%3D%3D) behind their use of Flutter, we partnered with Global Citizen to challenge our communities to design, build, and publish sustainability games using Flutter. These games are meant to inspire and empower players to take small, but meaningful actions for the environment. Today, we’re announcing [the 10 winners](https://medium.com/flutter/announcing-the-winners-of-the-global-gamers-challenge-1ccf4d271226). + +<YoutubeEmbed id="_GgqdB9md1w" title="" fullwidth="true"/> + + +Congrats to all of the winners! + +## Much more to explore + +That’s all we have time to spotlight today, but there are many more exciting improvements that we didn’t touch on. Here are a few more things to know: + +* You can ****preview [**the Vertex AI for Firebase Dart SDK**](https://firebase.google.com/docs/vertex-ai/get-started?platform=flutter) to use the Gemini API for AI-driven features in a Dart or Flutter app. The SDK is integrated with Firebase App Check, which protects your API calls, and safeguards your backend infrastructure from serious threats like billing fraud, phishing, and app impersonation. For details, see our [AI page](http://flutter.dev/ai). + +* **Impeller**, our [next-gen rendering engine](https://docs.flutter.dev/perf/impeller), is now feature complete on Android. + +* **Android’s [predictive back](https://developer.android.com/guide/navigation/custom-back/predictive-back-gesture)** gesture is now supported both when navigating within a Flutter app, and when navigating to another app or to the homescreen. + +* **Platform views** on iOS have been performance optimized, reducing CPU usage by up to 50%. + +* The **Google Mobile Ads SDK** has been extended to support more ad partners and mediation options. + +* DevTools has a new **deep link validator** that helps you identify and troubleshoot errors in Android deep link configuration, so you can more easily connect web experiences to a Flutter app. + +* **Dart fix**, our API migration tool, can now be invoked directly from the IDE. + +* **DevTools** now supports advanced filtering and CPU samples in the timeline. + +Head over to the [Flutter technical post](https://medium.com/flutter/fbde6c164fe3) and the [Dart 3.4 post](https://medium.com/dartlang/dart-3-4-bd8d23b4462a) for more details on each of these, and more. + +As always, we’re incredibly grateful for your continued support, passion, enthusiasm, and feedback. This project wouldn’t be possible without you, and we can’t wait to see how you continue to push Flutter forward. + +Flutter over and out. We hope to see you on board again soon! \ No newline at end of file diff --git a/sites/www/content/blog/learn-flutter-for-free-with-flutter-apprentice/images/1AvOHYfnMrCR81c4GltakzQ.webp b/sites/www/content/blog/learn-flutter-for-free-with-flutter-apprentice/images/1AvOHYfnMrCR81c4GltakzQ.webp new file mode 100644 index 0000000000..ba1530c520 Binary files /dev/null and b/sites/www/content/blog/learn-flutter-for-free-with-flutter-apprentice/images/1AvOHYfnMrCR81c4GltakzQ.webp differ diff --git a/sites/www/content/blog/learn-flutter-for-free-with-flutter-apprentice/images/1n9wUoadF-zJLDqvZ09oXvw.webp b/sites/www/content/blog/learn-flutter-for-free-with-flutter-apprentice/images/1n9wUoadF-zJLDqvZ09oXvw.webp new file mode 100644 index 0000000000..68335d7267 Binary files /dev/null and b/sites/www/content/blog/learn-flutter-for-free-with-flutter-apprentice/images/1n9wUoadF-zJLDqvZ09oXvw.webp differ diff --git a/sites/www/content/blog/learn-flutter-for-free-with-flutter-apprentice/index.md b/sites/www/content/blog/learn-flutter-for-free-with-flutter-apprentice/index.md new file mode 100644 index 0000000000..4dae651bb9 --- /dev/null +++ b/sites/www/content/blog/learn-flutter-for-free-with-flutter-apprentice/index.md @@ -0,0 +1,35 @@ +--- +title: "Learn Flutter for free with Flutter Apprentice!" +description: "A free book for learning Flutter, available for a time-limited period." +publishDate: 2021-10-06 +author: sfshaza2 +image: images/1AvOHYfnMrCR81c4GltakzQ.webp +category: tutorial +layout: blog +--- + +<DashImage figure src="images/1AvOHYfnMrCR81c4GltakzQ.webp" /> + + +We’ve heard from many folk that they want to learn Flutter, but don’t know where to start. So we have some great news for you — we’re giving away a free book for the next three months, along with a book club to help track your progress and answer questions. + +*Flutter Apprentice* is written to build on mobile development fundamentals. It takes you through your first fully-featured Flutter app, including designing a complex UI, as well as more advanced concepts such as persistence, state management, and cloud storage with Firebase. The book even covers publishing on both iOS and Android platforms. + +Flutter Apprentice comes from [Razeware](https://www.raywenderlich.com/), the team behind the raywenderlich.com books, videos and tutorials. The book normally costs $60 to purchase, but you’ll have free access to *Flutter Apprentice* from today, October 6, 2021 through January 6, 2022. + +Flutter Apprentice is a practical book, with lots of examples to follow and code that you can put to use in your own apps. And it’s based on the very latest Flutter 2.5 release and the latest features in the Dart language. Whether you’re an experienced developer looking to deepen your understanding of Flutter’s more advanced features, or you’re new to app development and interested in adding Flutter to your front-end development toolkit, we think you’ll find plenty of useful content . + +<DashImage figure src="images/1n9wUoadF-zJLDqvZ09oXvw.webp" /> + + +**Learn Together** + +We’re also excited to host the [*Flutter Apprentice Book Club*](https://www.youtube.com/playlist?list=PL4dBIh1xps-HAaadBRWQobCO_IJ4gMOG2), a weekly opportunity to hear live discussion and have your questions answered by the book’s authors and community experts. We’ll be partnering with Flutteristas and other prominent leaders in the Flutter community to host the book club. Join us each Wednesday at 12pm EST / 9am PST on the [Flutter Community YouTube channel](https://www.youtube.com/c/FlutterCommunityVideos) for summaries, and discussions. + +[Stay tuned ](https://twitter.com/FlutterDev)for pop quizzes, chances to win your very own Dash plushie, AMAs with the book’s authors and more. + +**Get Access** + +To get started, go to [flutter.dev/apprentice-giveaway](https://flutter.dev/apprentice-giveaway), where you’ll find instructions on accessing the book. + +You can also subscribe to [updates from the Flutter team](https://flutter.dev/subscribe). We’re looking forward to joining you on the journey; see you along the way! \ No newline at end of file diff --git a/sites/www/content/blog/learn-flutter-for-free/images/03uBFOes60gr9dEUa.webp b/sites/www/content/blog/learn-flutter-for-free/images/03uBFOes60gr9dEUa.webp new file mode 100644 index 0000000000..6a01ac5844 Binary files /dev/null and b/sites/www/content/blog/learn-flutter-for-free/images/03uBFOes60gr9dEUa.webp differ diff --git a/sites/www/content/blog/learn-flutter-for-free/images/0li3Xi9x6pYTCZYE2.webp b/sites/www/content/blog/learn-flutter-for-free/images/0li3Xi9x6pYTCZYE2.webp new file mode 100644 index 0000000000..6773e78350 Binary files /dev/null and b/sites/www/content/blog/learn-flutter-for-free/images/0li3Xi9x6pYTCZYE2.webp differ diff --git a/sites/www/content/blog/learn-flutter-for-free/images/1rVqP5EWrGnR_h0cB63qbFg.webp b/sites/www/content/blog/learn-flutter-for-free/images/1rVqP5EWrGnR_h0cB63qbFg.webp new file mode 100644 index 0000000000..42006bf99d Binary files /dev/null and b/sites/www/content/blog/learn-flutter-for-free/images/1rVqP5EWrGnR_h0cB63qbFg.webp differ diff --git a/sites/www/content/blog/learn-flutter-for-free/index.md b/sites/www/content/blog/learn-flutter-for-free/index.md new file mode 100644 index 0000000000..373e372740 --- /dev/null +++ b/sites/www/content/blog/learn-flutter-for-free/index.md @@ -0,0 +1,33 @@ +--- +title: "Announcing a free Flutter introductory course" +description: "Ten hours of video and hands-on tutorials to build beautiful apps" +publishDate: 2020-04-08 +author: aguinis +image: images/0li3Xi9x6pYTCZYE2.webp +category: spotlight +layout: blog +--- + +As we navigate through the current global crisis together, we know a lot of people are looking to develop new skills. We want to help, so we’re announcing a partnership with the App Brewery to give away [a new introductory course](https://www.appbrewery.co/courses/intro-to-flutter) on Flutter, offering free access to the course to those who activate it within the next three months. This course requires no prior Flutter experience, and we hope this gives you the chance to build new development skills for iOS, Android, and the web. + +<DashImage figure src="images/0li3Xi9x6pYTCZYE2.webp" /> + + +[Flutter](http://flutter.dev) is Google’s UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase. Over [a million developers worldwide](https://youtu.be/REJDzio_h7o) are using it, from individuals and startups to big companies like Alibaba, Capital One, and eBay. + +We are giving away the [*Introduction to Flutter Development*](https://www.appbrewery.co/courses/intro-to-flutter) course for free with lifetime access if activated during the next three months. The course covers some of the fundamental concepts for Flutter development, teaching you how to code using Dart and walking you through building nine real-world apps that run on iOS and Android, even if you have no programming experience. + +The course instructor, Angela Yu, takes you step-by-step through engaging online lessons that give you practical hands-on experience to succeed as a Flutter developer. The course includes 10+ hours of video tutorials and labs, during which you’ll build your programming knowledge and make real world apps such as a Choose Your Own Adventure game and a quiz app. You’ll even build a musical instrument! + +<DashImage figure src="images/03uBFOes60gr9dEUa.webp" /> + + +We are expecting a lot of people to take this course, so we have also worked with [Very Good Ventures](https://verygood.ventures), one of the top Flutter development agencies, to add concierge support for questions and help in the associated Discord channel. + +By the end of this course, you’ll be ready to start building your own Flutter apps and on your way to becoming a fully fledged Flutter developer. If you get through this course, then there are many more modules available for further learning on [App Brewery](https://www.appbrewery.co/). You’ll end the course with a certificate of completion, along with a portfolio of apps that have taught you the fundamentals of working with Flutter and Dart. + +We hope to see you there soon and that you’ll share your progress using #FreeFlutterCourse. + +Enroll Here: [https://www.appbrewery.co/courses/intro-to-flutter](https://www.appbrewery.co/courses/intro-to-flutter) + +<DashImage figure src="images/1rVqP5EWrGnR_h0cB63qbFg.webp" alt="*Introduction to Flutter Development* Course Syllabus" caption="*Introduction to Flutter Development* Course Syllabus" /> diff --git a/sites/www/content/blog/learn-how-to-build-agentic-apps-with-flutter-angular-firebase-and-vertex-ai/images/0bWWt_FlITTVh_813.webp b/sites/www/content/blog/learn-how-to-build-agentic-apps-with-flutter-angular-firebase-and-vertex-ai/images/0bWWt_FlITTVh_813.webp new file mode 100644 index 0000000000..c4dea3bc2f Binary files /dev/null and b/sites/www/content/blog/learn-how-to-build-agentic-apps-with-flutter-angular-firebase-and-vertex-ai/images/0bWWt_FlITTVh_813.webp differ diff --git a/sites/www/content/blog/learn-how-to-build-agentic-apps-with-flutter-angular-firebase-and-vertex-ai/index.md b/sites/www/content/blog/learn-how-to-build-agentic-apps-with-flutter-angular-firebase-and-vertex-ai/index.md new file mode 100644 index 0000000000..938bd2919e --- /dev/null +++ b/sites/www/content/blog/learn-how-to-build-agentic-apps-with-flutter-angular-firebase-and-vertex-ai/index.md @@ -0,0 +1,39 @@ +--- +title: "Learn how to build agentic apps with Flutter, Angular, Firebase, and Vertex AI" +description: "The way we build applications is evolving. Imagine apps that don’t just respond to user input but proactively assist, automate tasks, and…" +publishDate: 2025-04-24 +author: kkboateng +image: images/0bWWt_FlITTVh_813.webp +category: spotlight +layout: blog +--- + +<DashImage figure src="images/0bWWt_FlITTVh_813.webp" /> + + +The way we build applications is evolving. Imagine apps that don’t just respond to user input but proactively assist, automate tasks, and create entirely new kinds of experiences. Welcome to the world of **agentic apps**! + +Today, we’re thrilled to announce the launch of starter kits for our latest learning series, **[Building Agentic Apps](https://flutter.dev/events/building-agentic-apps),** running from today, **April 23rd to May 14th, 2025**! It’s three weeks of new resources, livestreams, and questions & answers, all aimed at giving Angular and Flutter developers the chance to spend a couple weekends hacking on AI tech and in the process gain an understanding of the building blocks of AI, know where and when to put them to work, and build a completed example worth showing off. + +<YoutubeEmbed id="24xNuDY0y0c" title="Build agentic apps in Flutter and Angular" fullwidth="true"/> + + +## **What are agentic apps?** + +An agentic app includes user experiences powered by an agent — a combination of a large language model (LLM), a goal, and the tools (like APIs or functions) needed to achieve the goal. Picture a Flutter travel app that doesn’t just show destinations but also can generate a personalized packing list based on the weather forecast and planned activities, or an app that automatically summarizes your unread emails into a prioritized to-do list. With agents, apps can offer more help, be more proactive, and be easier to use than ever before. + +## **Why now?** + +Because there’s no better time to become acquainted with the latest AI tools and see where they can (or can’t) help you deliver value to your users. By the end of three weeks, you’ll have built out an agentive experience and have a good handle on the terminology, the design patterns, and which user journeys are the most suited to AI, even if you’re a new developer! + +**What to Expect During the Campaign (April 23 — May 14):** + +* **Starter Kits:** We’re releasing four open-source starter projects for Angular and Flutter developers, combining both with Genkit and Vertex AI in Firebase. They’re designed to give you the structure of an agentic experience that you can easily understand, modify, and repurpose. + +* **Expert Guidance:** Join livestreams and online events featuring engineers from the Flutter and Angular developer communities, Firebase, and Google AI teams. Ask questions, see live coding, and learn best practices. + +* **Community:** Connect with fellow developers, share your progress, and see what amazing things the community builds. We’re partnering with Google’s [Build With AI](https://developers.google.com/community) series to make sure their in-person events have access to the new resources. + +## **Get Started Today!** + +Register now by heading to [goo.gle/BuildingAgenticFlutter](http://goo.gle/BuildingAgenticFlutter) or [goo.gle/BuildingAgenticAngular](http://goo.gle/BuildingAgenticAngular). We can’t wait to see what you build! \ No newline at end of file diff --git a/sites/www/content/blog/learn-testing-with-the-new-flutter-sample-gsoc20-work-product/images/0d_ULBZHcg78XQFJb.gif b/sites/www/content/blog/learn-testing-with-the-new-flutter-sample-gsoc20-work-product/images/0d_ULBZHcg78XQFJb.gif new file mode 100644 index 0000000000..446609dade Binary files /dev/null and b/sites/www/content/blog/learn-testing-with-the-new-flutter-sample-gsoc20-work-product/images/0d_ULBZHcg78XQFJb.gif differ diff --git a/sites/www/content/blog/learn-testing-with-the-new-flutter-sample-gsoc20-work-product/images/15PoXySK4-tAKWAY1zCzfHg.webp b/sites/www/content/blog/learn-testing-with-the-new-flutter-sample-gsoc20-work-product/images/15PoXySK4-tAKWAY1zCzfHg.webp new file mode 100644 index 0000000000..5c8f0bdc75 Binary files /dev/null and b/sites/www/content/blog/learn-testing-with-the-new-flutter-sample-gsoc20-work-product/images/15PoXySK4-tAKWAY1zCzfHg.webp differ diff --git a/sites/www/content/blog/learn-testing-with-the-new-flutter-sample-gsoc20-work-product/index.md b/sites/www/content/blog/learn-testing-with-the-new-flutter-sample-gsoc20-work-product/index.md new file mode 100644 index 0000000000..0a9294f077 --- /dev/null +++ b/sites/www/content/blog/learn-testing-with-the-new-flutter-sample-gsoc20-work-product/index.md @@ -0,0 +1,86 @@ +--- +title: "Learn testing with the new Flutter sample | GSoC’20 Work Product" +description: "By Abdullah Deshmukh" +publishDate: 2020-08-29 +author: abd99 +image: images/15PoXySK4-tAKWAY1zCzfHg.webp +category: events +layout: blog +--- + +<DashImage figure src="images/15PoXySK4-tAKWAY1zCzfHg.webp" /> + + +## A quick introduction to GSoC + +[Google Summer of Code](https://summerofcode.withgoogle.com/)(GSoC) is a 3-month program sponsored by Google in which students work with an open source organization. With the help of mentors, the students complete a project during their summer break. + +This year, the Dart team participated for the first time as a mentoring organization in GSoC. [Five Dart project proposals](https://summerofcode.withgoogle.com/organizations/6544944065413120/) were accepted by GSoC, of which two featured Flutter. + +This blog shows the work I did for my GSoC project. [Check out a summary of the work done by all the students under Dart](https://link.medium.com/s1jpFEG3fab). All of the resulting source code is in GitHub repositories, and you can see how the work progressed by looking at individual pull requests (PRs). + +## Project details: Testing sample app + +Out of my two submitted proposals, the [Testing sample app](https://github.com/dart-lang/sdk/wiki/Dart-GSoC-2020-Project-Ideas#idea-flutter-testing-sample-app) was accepted. Testing is the most common thing skipped by students when learning any new technology. This project focuses on helping developers learn testing in Flutter in an easy, quick way. It also aims to help students see the best practices of testing in Flutter. [Check out the testing_app sample on GitHub](https://github.com/flutter/samples/tree/master/testing_app). + +<DashImage figure src="images/0d_ULBZHcg78XQFJb.gif" alt="A screen-capture of the app in action" caption="A screen-capture of the app in action" /> + + +We built an app that contains a list of items. The user can add items to, and remove items from, a favorites list. Then we used a tool named Flutter Driver to perform several types of testing. (Flutter Driver lets you create instrumented apps and drive those apps from a test suite.) We demonstrated the following types of testing: + +### **Widget testing** + +A type of testing where you can test each and every individual widget of your choice. It is important to know whether each individual widget is working as intended. + +For more info on Flutter widget testing, see [An introduction to widget testing](https://flutter.dev/docs/cookbook/testing/widget/introduction). + +Related PR: [#476](https://github.com/flutter/samples/pull/476) + +### **Integration testing (Flutter Driver)** + +Using Flutter Driver for integration testing is similar to using Selenium WebDriver (generic web), Protractor (Angular), Espresso (Android), or Earl Gray (iOS). Integration testing with Flutter Driver gives us a good idea of how different pieces of our app perform as a whole on a real device. + +For more info on Flutter integration testing, see [An introduction to integration testing](https://flutter.dev/docs/cookbook/testing/integration/introduction). + +Related PR: [#481](https://github.com/flutter/samples/pull/481) + +### **Performance testing** + +Flutter Driver lets us trace the performance of the app on real devices and store the entire timeline of the task. + +For more info on Flutter performance testing, see [Performance profiling](https://flutter.dev/docs/cookbook/testing/integration/profiling). + +Related PR: [#494](https://github.com/flutter/samples/pull/494) + +### **State management testing** + +Testing state is quite important, especially in complex apps where a lot of state data is present and a dedicated state management approach is used. This app manages state using the [provider package](https://pub.dev/packages/provider). The sample demonstrates how to test state using the Flutter Driver. + +Related PRs: **[**#487](https://github.com/flutter/samples/pull/487), [#490](https://github.com/flutter/samples/pull/490) + +To see how the testing sample progressed during the GSoC period, see [this set of PRs](https://github.com/flutter/samples/pulls?q=author%3Aabd99+created%3A2020-06-01..2020-08-31+). + +As development of the sample was coming to end, I knew that I would have a lot of time left under the GSoC timeline with not much work. So, I reached out to my mentors, Brett and Andrew, asking for more to do. Thanks to Flutter being an open source project, I had a lot of things to choose from. Out of the projects suggested by my mentors, I felt that building a codelab on “Testing in Flutter” was the most interesting, and it was related to the topic of my project as well. So, I spent the rest of the time building the codelab. [Check it out](https://codelabs.developers.google.com/codelabs/flutter-app-testing). + +## Experience + +Google Summer of Code has been one of the best experiences of my entire life. I never thought my summer would be so amazing at the start of the year — full of learning and at the same time contributing to an amazing community. + +### Mentorship + +[Brett](https://github.com/domesticmouse), my primary mentor, was everything I could ask for in a mentor/co-worker. He replied to my emails within a few minutes. Yes, you read that right, minutes, even though we had a significant difference in our time zones. He answered my questions in a way that would help me find the answers to the questions myself. He showed me how building good resources for developers is supposed to be done in the best way. The most important point is the story — the way in which you plan to share the knowledge in the form of a sample, codelab, etc. He also helped with career-related questions and connected me to others when he wasn’t able to help with a specific problem. And yes, he is the coolest person I have ever worked with. + +[Andrew](https://github.com/RedBrogdon), my secondary mentor, was also always there to help me with my questions, be they project related or career related. He helped me take an approach that would solve my problem in an easy and compelling way, always keeping in mind the perspectives of the community and myself. + +### Learning + +GSoC is primarily a learning-focused program and always will be. I personally learned a lot from the entire program. I learned how to build a sample intended to teach something while keeping everything else as simple as possible, how to build a codelab (which you can do too, using [these codelab authoring and tools](https://github.com/googlecodelabs/tools)), and much more. + +### Challenges + +It’s always said that there’s no growth/learning without challenges. The first challenge I faced was to keep the sample extremely simple, but at the same time as realistic as possible. The next challenge was when I tried to run Flutter Driver tests using Travis CI, as I had no prior experience with CI (continuous integration) systems. We were able to get CI running, but we decided to not include it in the sample because Flutter Driver tests are supposed to be run on a physical device and not on an emulator/simulator. + +Lastly, I would like to thank Brett, Andrew, the Flutter team, and the GSoC team for the amazing experience I had during this summer. + +Thanks for reading! +> About the author: Abdullah is a senior year undergrad student based in Pune, India. He has a keen interest in Mobile App Engineering and a lot of other things built around it. You can connect with him on [Twitter](https://twitter.com/abdullahzakir99), [LinkedIn](https://www.linkedin.com/in/abdullahzakir99/), and [GitHub](https://github.com/abd99). \ No newline at end of file diff --git a/sites/www/content/blog/learn-to-build-native-mobile-apps-with-flutter-with-our-free-udacity-course/images/1v_vdCNpmwHNcOM0xiqXIOQ.webp b/sites/www/content/blog/learn-to-build-native-mobile-apps-with-flutter-with-our-free-udacity-course/images/1v_vdCNpmwHNcOM0xiqXIOQ.webp new file mode 100644 index 0000000000..583e54f016 Binary files /dev/null and b/sites/www/content/blog/learn-to-build-native-mobile-apps-with-flutter-with-our-free-udacity-course/images/1v_vdCNpmwHNcOM0xiqXIOQ.webp differ diff --git a/sites/www/content/blog/learn-to-build-native-mobile-apps-with-flutter-with-our-free-udacity-course/index.md b/sites/www/content/blog/learn-to-build-native-mobile-apps-with-flutter-with-our-free-udacity-course/index.md new file mode 100644 index 0000000000..5b7d33a87e --- /dev/null +++ b/sites/www/content/blog/learn-to-build-native-mobile-apps-with-flutter-with-our-free-udacity-course/index.md @@ -0,0 +1,57 @@ +--- +title: "Learn to build native mobile apps with Flutter with our free Udacity course!" +description: "TL;DR: Sign up here to get started with our free Udacity course!" +publishDate: 2018-07-02 +author: maryx +image: images/1v_vdCNpmwHNcOM0xiqXIOQ.webp +category: tutorial +layout: blog +--- + +<DashImage figure src="images/1v_vdCNpmwHNcOM0xiqXIOQ.webp" /> + + +Hi everyone! I’m Mary, a software engineer who works with Flutter at Google. I used to work on web development, and transitioned to learning and building with Flutter over the past year. It’s been fun and interesting to build with Flutter, and I wanted to share my knowledge. That’s why the Flutter team at Google, Udacity, and I collaborated to create the free online video course, [“Build Native Mobile Apps with Flutter”](https://udacity.com/course/ud905). I injected my “learning Flutter” experience into the course, which teaches you what Flutter is, and how to (intuitively!) build mobile apps for both iOS and Android. + +This free course is for all developers; you might be a mobile app developer looking for ways to reach users on all platforms, or a web developer who wants to build for multiple platforms (like me!). Or, maybe you’ve been interested in Flutter but haven’t had time to dig in, or don’t know where to start. Well, you can [sign up for the course](https://udacity.com/course/ud905) and get watching right away! All the code is open source and located [on GitHub](https://github.com/flutter/udacity-course). Each topic is packaged into nicely-sized chunks, with short videos, quizzes, and small coding projects. You can pick it up and pause anytime. Complete the course at your leisure, such as over a weekend, or split it into several sections. + +What will you learn? The first lesson acquaints you with Dart and Flutter. You’ll learn about widgets: building your own, managing state, and how to use the Flutter tooling (like the Flutter Inspector). You’ll customize your app in the second lesson with Material Design, fonts, icons, and real data from an API, to turn your toy app into a functioning unit converter. Each coding project is self-contained, with both a task and one possible solution (out of many). + +Throughout the course, you’ll incrementally build a unit converter app. Yes, I hear you protesting about the apparent lack of creativity in such an endeavor. But, there are many open-ended areas to explore if you choose — such as customizing colors, shapes, theming, APIs, animations, and more. I’ve even added a list of goofy-but-real unit conversions to the mix, in case you’ve ever wanted to convert the mass of a bag of cement into that of a burrito*. Still, if you follow the course exactly, you’ll learn the Flutter fundamentals, and build a lovely unit converter to share with your friends, on any device! It’s a flexible course, so if you already have some Flutter experience, skip ahead, or delve more into a specific topic; we have linked to various resources where you can learn more. + +** This is unofficial, please don’t mention I snuck it in.* + +Why a course for Flutter? It’s a great development platform! Some of the coolest Flutter features, in my opinion, include: + +**Hot Reload** +I developed for mobile years ago, and I spent more time waiting for the app to build than actually coding. Hot reload means that your code changes are reflected in your app the moment you save, preserving the current app state. This is so useful for iterating on a UI; it almost feels like I’m back in web again! + +**Intuitive UI Layout** +Centering an item, and implementing sticky footers or efficient lists have often been challenging. With Flutter, centering is literally wrapping the item you want to center in a Center widget. There are numerous other widgets such as Stack, AspectRatio and ListTile, that make it intuitive to lay out your UI. It might feel a little odd initially (Opacity and Padding are both widgets, for example), but after you check out the [documentation](https://flutter.io/docs), [Flutter cookbook](https://flutter.io/cookbook), and [StackOverflow](https://stackoverflow.com/questions/tagged/flutter), you’ll get the hang of it! + +**Widget Customization and Extension** +One time, I needed a slider for my app. Great, Flutter comes with a Slider widget! Boo, the widget didn’t let me customize its inactive color. I made a pull request to the Slider class, which added the inactive color property, and hooray, I extended the widget! This is just one way to extend existing widgets — you can also build custom widgets by composing widgets, as we do with the CategoryTile in the course. + +## **Get involved in the Flutter Community** + +This course is meant to be a community project. Consider contributing your own tips and tricks, perhaps by writing a blog post, or making your own screencast. You’ll learn a lot through this process — I learned not only about Flutter, but also how to teach and lead. I discovered new ways to think about widgets, and how to mix-and-match them. Although we had fancy animations in our videos, anyone (including you!) can record a how-to video. + +Feel free to contribute in any of the following ways: + +* Asking and answering questions on [GitHub](https://github.com/flutter/flutter), [StackOverflow](https://stackoverflow.com/questions/tagged/flutter), [Gitter](https://gitter.im/flutter/flutter), and the [Udacity forum](https://discussions.udacity.com/c/standalone-courses/native-mobile-apps-flutter). + +* Writing blog posts and code snippets, and posting them to [Reddit](https://www.reddit.com/r/FlutterDev) and the [Flutter mailing list](https://groups.google.com/forum/#!forum/flutter-dev). + +* Making your own Flutter how-to video. + +* Creating a local meetup in your area or school. + +## **Next steps** + +Once you complete [“Build Native Apps with Flutter”](https://udacity.com/course/ud905), try our [Flutter codelabs](https://codelabs.developers.google.com/?cat=Flutter), watch our [Dev Bytes](https://www.youtube.com/watch?v=fq4N0hgOWzU&list=PLOU2XLYxmsIJ7dsVN4iRuA7BT8XHzGtCr) and [browse our website](https://flutter.io). + +[Join our community!](https://flutter.io/support) Tell us about your app from the link on our [Showcase](https://flutter.io/showcase/) page, or tweet us at [@flutter.io](https://twitter.com/flutterio). + +## Ok, now power on that computer, [download that Flutter plugin](https://flutter.io/get-started), and get coding! ***We can’t wait to see what you build!*** + +*An enormous THANK YOU to Flutter, Material Design, Udacity, and all our volunteers for making this course possible!* \ No newline at end of file diff --git a/sites/www/content/blog/learning-faster-with-antigravity/images/19kmUzKkC8c8EBCvMQKftBg.webp b/sites/www/content/blog/learning-faster-with-antigravity/images/19kmUzKkC8c8EBCvMQKftBg.webp new file mode 100644 index 0000000000..0955d7a4d7 Binary files /dev/null and b/sites/www/content/blog/learning-faster-with-antigravity/images/19kmUzKkC8c8EBCvMQKftBg.webp differ diff --git a/sites/www/content/blog/learning-faster-with-antigravity/images/1GMT5ZFE5c7DhhwEqm7_iOw.webp b/sites/www/content/blog/learning-faster-with-antigravity/images/1GMT5ZFE5c7DhhwEqm7_iOw.webp new file mode 100644 index 0000000000..bacd2ab5c7 Binary files /dev/null and b/sites/www/content/blog/learning-faster-with-antigravity/images/1GMT5ZFE5c7DhhwEqm7_iOw.webp differ diff --git a/sites/www/content/blog/learning-faster-with-antigravity/images/1MuEwLr96JNe25xfg6kc4Rw.webp b/sites/www/content/blog/learning-faster-with-antigravity/images/1MuEwLr96JNe25xfg6kc4Rw.webp new file mode 100644 index 0000000000..61232bb130 Binary files /dev/null and b/sites/www/content/blog/learning-faster-with-antigravity/images/1MuEwLr96JNe25xfg6kc4Rw.webp differ diff --git a/sites/www/content/blog/learning-faster-with-antigravity/images/1XhysKMaIjv--tjhGDEas6A.webp b/sites/www/content/blog/learning-faster-with-antigravity/images/1XhysKMaIjv--tjhGDEas6A.webp new file mode 100644 index 0000000000..75af0bfd2c Binary files /dev/null and b/sites/www/content/blog/learning-faster-with-antigravity/images/1XhysKMaIjv--tjhGDEas6A.webp differ diff --git a/sites/www/content/blog/learning-faster-with-antigravity/index.md b/sites/www/content/blog/learning-faster-with-antigravity/index.md new file mode 100644 index 0000000000..4384087549 --- /dev/null +++ b/sites/www/content/blog/learning-faster-with-antigravity/index.md @@ -0,0 +1,197 @@ +--- +title: "Learning faster with Antigravity" +description: "Creating Flutter frontends for ADK" +publishDate: 2026-07-01 +author: redbrogdon +image: images/1XhysKMaIjv--tjhGDEas6A.webp +layout: blog +--- + +<DashImage figure src="images/1XhysKMaIjv--tjhGDEas6A.webp" alt="Dash enjoying Antigravity" caption="Dash enjoying Antigravity" /> + + +## Learning faster with Antigravity + +### Creating Flutter frontends for ADK + +How can I build a Flutter frontend for an agent when that agent is built with an SDK and a language I’ve never used before? + +This was the challenge I faced when approaching a Python-based agent written with Agent Development Kit (ADK). With limited experience in Python and no prior exposure to the ADK framework, building a client that integrates with the backend server presented a significant learning curve. Plus, even if I could get a coding agent to crank out something that worked, finishing the project without understanding the code was also a form of failure. + +After a few false starts, though, I found an answer. Using a structured, iterative workflow with my AI coding partner, Antigravity, I created a reusable developer skill that codified what I learned with each go-round. I started from scratch, generated notes about the code, created multiple apps that connected to the [`deep_search`](https://github.com/google/adk-samples) agent (a multi-agent research coordinator from the official ADK samples repository), and incrementally built up the skill and my own understanding. At times, I was using multiple agents at the same time, an “author” agent to create the skill with me, and a “coder” agent to use that guidance to build frontends. + +What I ended up with was an agent skill called [`flutter_frontend_for_adk`](https://github.com/flutter/demos/tree/main/flutter-frontend-for-adk). It includes five reference docs that guide Antigravity through a sequence of phases, each one ending in a deliverable. The first phases generated the following notes files, so that I could structure how the “coder” agent thought about the task as it analyzed the agent and prepared to generate the app: + +* `AGENT_INTERFACE_NOTES.md` — Notes taken during an analysis of the agent’s source code. What is it meant to accomplish and how is it constructed? What are the interfaces and APIs this agent exposes and how do they work? + +* `FRONTEND_USAGE_NOTES.md` — The first spec. What should the frontend do and how should users interact with it? + +* `FRONTEND_ARCHITECTURE_NOTES.md` — An architecture plan. What services, classes, state, and models should be created? + +* `FRONTEND_DESIGN_NOTES.md` — A design document for the frontend. What will it look like? What are the colors, fonts, and other details? + +After that, Antigravity could generate the application’s code, run, and test it. While the skill (and I) remain a work in progress, I’ve now got a decent handle on ADK, as well as a useful way to build frontends for existing agents. + +This is how it happened. + +<DashImage figure src="images/19kmUzKkC8c8EBCvMQKftBg.webp" alt="Screenshot of a generated frontend for the deep_search agent" caption="Screenshot of a generated frontend for the deep_search agent" /> + + +## Start with the work of others + +I’m not the only one writing skills around here, so the first step was to install some skills from the Google Cloud and Flutter teams: + +```bash +npx skills add google/agents-cli --skill google-agents-cli-adk-code +npx skills add flutter/skills +``` + + +Those got me some basic smarts about ADK, its CLI tool, and Flutter. I also had the Dart MCP server installed, thanks to the Dart extension for Antigravity. + +## Establish the loop + +Rather than begin with a request for the agent to blast out an app, I established a kind of “learning loop” with it. First, I asked the coder agent to execute the workflow from my skill, and then I worked with the author agent to manually evaluate the coder’s output, identify gaps in the skill’s instructions, and update the guidelines to improve future runs. After each iteration I deleted the generated notes and app, started over, and discovered the next thing I needed to learn. + +<DashImage figure src="images/1GMT5ZFE5c7DhhwEqm7_iOw.webp" alt="Behold my loop!" caption="Behold my loop!" /> + + +The loop consists of five distinct steps driven by conversations with Antigravity: + +**1) Execute the current skill**: In a fresh conversation, the coder agent runs the workflow defined by the current version of the skill file. + +My prompt: `This codebase includes an agent built with ADK, but no frontend or client. I’d like you to read the instructions in flutter-frontend-for-adk/SKILL.md and follow the workflow. Do not examine git history, do not reference the context of other conversations we’ve had, and do not change git branches.` + +**2) Evaluate output and specifications**: In a second conversation, the author agent and I inspect the generated deliverables, such as architectural notes, design specifications, and the resulting code structure. I was better at noticing structural issues, while Antigravity helped catch all the little stuff. + +My prompt: `While I review the notes and code, take a look at the generated artifacts and tell me how well you think they match the instructions and our goals.` + +**3) Identify gaps and failure modes**: We identify areas where the coder agent lacked specific guidance, made incorrect assumptions, or encountered integration issues. + +My prompt: `I noticed two issues in the last run: first, the root gitignore is ignoring the new frontend/lib/ folder due to a global ‘lib/’ ignore rule. Second, the coder agent is executing all six phases continuously without stopping. We need a way to pause and verify the deliverables after each stage.` + +**4) Update central skill and references**: We update the instructions in SKILL.md or the sub-guides in the references/ directory to address the gaps. + +My prompt: `Let’s update Phase 6 in SKILL.md to explicitly handle the gitignore issue by adding ‘!frontend/lib’ if ‘lib/’ is ignored globally. Also, add a mandatory ‘Review’ step at the end of Phases 1 to 4 instructing the agent to pause and wait for my approval before proceeding.` + +**5) Clear local files and rerun**: I delete temporary specification files, reset the git working tree, and head back to the top of the loop. + +While working with a pair of agents definitely sped things up, it didn’t make the process instantaneous. I can only absorb so much information at a time! Within each iteration, though, I read through notes and source files, reviewed the conversation with Antigravity, and (once I was generating real code) ran the app to see how it performed. + +At the start, I was mostly reading and learning. By the end, though, I knew enough about what was going on to zero in on things that were failing consistently, start doing my own research, and tweak code to fix issues. After I was done modifying the notes and codebase, I’d ask Antigravity how it could update the skill to make future code generation look more like what I’d put together. As a result, the skill wasn’t just instructions for the coder agent, but a record of what I was learning. + +## Loop the loop + +It took thirteen iterations to get me to something that I’m not embarrassed to share. Here are the first six, which were mostly about discovery and note-taking: + +1. **Bootstrapping and workspace mapping** — I realized that a single, monolithic `SKILL.md` file was too hard for the agent to navigate. I refactored the skill to be modular, creating a dedicated references/ folder and writing the first specialized guide: `references/agent_discovery.md`. That got Antigravity through analyzing the agent’s source and creating `AGENT_INTERFACE_NOTES.md` to record what it learned. + +1. **Defining behavior and user experience** — Next up was the usage spec for the frontend: What would it do? How would a user interact with it? I added a new phase to the skill, instructed it to read references/frontend_usage.md and interview me for platform preferences and feature requirements. This got me `FRONTEND_USAGE_NOTES.md`, which defined the app’s purpose and feature set before we ever touched the code. + +1. **Setting the structural blueprint** — Architecture. We added `references/frontend_architecture.md` to the skill, which focused on architectural details that the frontend would need, and I got the agent to produce `FRONTEND_ARCHITECTURE_NOTES.md`. + +1. **Designing the visual aesthetics** — We created `references/frontend_design.md` to instruct the agent on how to define visual aesthetics and added scaffolding instructions to `SKILL.md`. This guide directed the agent to interview me for theme, breakpoint, and animation preferences, and Antigravity started producing `FRONTEND_DESIGN_NOTES.md`. + +1. **Generating and running an app** — At this point, I was able to tell Antigravity to start generating the actual flutter code based on all the specifications we had gathered. The app failed pretty spectacularly, and I realized some kind of general “best practices” file was needed to get it over problems that were likely to reappear. We added `references/frontend_best_practices.md` to the skill files to guide the actual coding, and started fixing things one at a time. + +From here on out, I continued iterating with Antigravity as I tackled one issue at a time, updating the code, updating the skill to match, and then wiping out the generated frontend to try again. In the process, the loop got tighter and the changes smaller. I did seven more iterations: + +1. **The app can’t make network calls!** — Fixed the entitlements and info for macOS and iOS. + +1. **All this markdown isn’t getting formatted!** — Took advantage of `flutter_markdown` to properly display text from the agent, which often contains markdown. + +1. **Why doesn’t the code look right** ?— Added lints, formatting rules, and other post-generation checks. + +1. **Sealed classes!** — Different message types should share a base type so the app can use exhaustive switch statements. + +1. **Why aren’t the chats scrolling?** — Added a `ScrollController` to automatically advance lists of messages when new ones arrive. + +1. **Why did the app crash when I ran it on the web?** — Removed `dart:io` and relied on `package:http` for networking. + +1. **Partial events in my list!** — ADK sends a stream of events, sometimes in pieces. The frontend needed to assemble and present them coherently rather than give the user a list of all the partials (with broken markdown formatting). + +1. **Why is the tool window blank?** — As generated, the apps weren’t dealing with tool naming conventions correctly, and the event handling wasn’t correctly identifying when tools were being invoked. + +## An example “thing that went wrong” + +To give you an idea of the work done in each of the iterations, let’s walk through one of the changes: how the frontend handles partial events. + +The ADK server streams events representing small chunks of content or partial tool execution stages. If the client application renders every incoming network event as an individual message bubble in the chat list, though, the interface becomes fragmented. A single response from the agent would appear as dozens of isolated, broken text blocks. + +**The old way (a naive list):** + +```dart +// Rendering every raw event chunk in a list view +ListView.builder( + itemCount: rawEvents.length, + itemBuilder: (context, index) { + // Each partial text chunk is rendered in a separate bubble + return ChatBubble(text: rawEvents[index].contentText); + }, +) +``` + + +This code leads to UI that looks like this: + +<DashImage figure src="images/1MuEwLr96JNe25xfg6kc4Rw.webp" alt="Look at all those broken list items and half-bolded segments!" caption="Look at all those broken list items and half-bolded segments!" /> + + +The fix for this isn’t particularly hard, once you know the right thing to do. I didn’t, of course, so I started asking Antigravity questions like “How does ADK use SSE to stream events?” and “What does the over-the-wire data structure for an event look like, and how do I know if it’s a partial event?” If “vibe learning” were a thing, I was doing it. + +It turns out that you just need to check a flag, so I tweaked the code in the frontend’s `AgentProvider` class, asked Antigravity to review it in case I’d forgotten anything obvious, and ran the app to verify. It took one more request to Antigravity to update the best practices so that every new frontend would use this approach. + +**The new way (aggregation in AgentProvider):** + +```dart +await for (final event in stream) { + if (event.partial && event.contentText != null) { + // Accumulate streaming text + _activeStreamingAuthor = event.author; + _activeStreamingResponse = + (_activeStreamingResponse ?? ‘’) + event.contentText!; + } else { + // Finished chunk: Clear accumulator and add to permanent events list + _activeStreamingResponse = null; + + final updatedEvents = List<Event>.from(_activeSession!.events) + ..add(event); + + // Merge state delta + Map<String, dynamic> mergedState = _extractSessionStateMap( + _activeSession!, + ); + if (event.actions.stateDelta.isNotEmpty) { + mergedState.addAll(event.actions.stateDelta); + } + + // Build a new copy of the Session with updated events & state + _activeSession = Session.fromJson({ + 'id': _activeSession!.id, + 'app_name': _activeSession!.appName, + 'user_id': _activeSession!.userId, + 'events': updatedEvents.map((ev) => _serializeEvent(ev)).toList(), + 'state': mergedState, + }); + + _processCompletedEvent(event); + } + + notifyListeners(); +} +``` + + +## Try it yourself + +I’m very much a “learn by doing” person, and trying something entirely outside my comfort zone (like both a new language and a new SDK) was pretty intimidating. Having this loop, though, gave me a structure to work off of, which was really helpful, and now I have a [neat new agent skill](https://github.com/flutter/demos/tree/main/flutter-frontend-for-adk). + +If you haven’t already, download Antigravity yourself and give it a try — it only took me a few hours, and I definitely picked up some new tricks. + +To get started: + +1. Download [Antigravity](https://antigravity.google) to integrate the agentic coding assistant into your development environment. + +1. Visit the [AGY getting started](https://antigravity.google/docs/getting-started) documentation to learn how to define your own custom developer skills and reference guidelines. + +1. Draft a simple skill for a framework you want to explore, and keep looping until you get something you like! \ No newline at end of file diff --git a/sites/www/content/blog/learning-flutters-new-navigation-and-routing-system/images/17-wvbHmckKFVagnUwsQI2g.webp b/sites/www/content/blog/learning-flutters-new-navigation-and-routing-system/images/17-wvbHmckKFVagnUwsQI2g.webp new file mode 100644 index 0000000000..2ac7a4c339 Binary files /dev/null and b/sites/www/content/blog/learning-flutters-new-navigation-and-routing-system/images/17-wvbHmckKFVagnUwsQI2g.webp differ diff --git a/sites/www/content/blog/learning-flutters-new-navigation-and-routing-system/images/1PYHrYurwAGyQC8vsnAaWiA.webp b/sites/www/content/blog/learning-flutters-new-navigation-and-routing-system/images/1PYHrYurwAGyQC8vsnAaWiA.webp new file mode 100644 index 0000000000..e34dc15fab Binary files /dev/null and b/sites/www/content/blog/learning-flutters-new-navigation-and-routing-system/images/1PYHrYurwAGyQC8vsnAaWiA.webp differ diff --git a/sites/www/content/blog/learning-flutters-new-navigation-and-routing-system/images/1hNt4Bc8FZBp_Gqh7iED3FA.webp b/sites/www/content/blog/learning-flutters-new-navigation-and-routing-system/images/1hNt4Bc8FZBp_Gqh7iED3FA.webp new file mode 100644 index 0000000000..f43010030f Binary files /dev/null and b/sites/www/content/blog/learning-flutters-new-navigation-and-routing-system/images/1hNt4Bc8FZBp_Gqh7iED3FA.webp differ diff --git a/sites/www/content/blog/learning-flutters-new-navigation-and-routing-system/images/1v77nG0BRIWrOghj8fCq_EA.webp b/sites/www/content/blog/learning-flutters-new-navigation-and-routing-system/images/1v77nG0BRIWrOghj8fCq_EA.webp new file mode 100644 index 0000000000..1418ea3c08 Binary files /dev/null and b/sites/www/content/blog/learning-flutters-new-navigation-and-routing-system/images/1v77nG0BRIWrOghj8fCq_EA.webp differ diff --git a/sites/www/content/blog/learning-flutters-new-navigation-and-routing-system/index.md b/sites/www/content/blog/learning-flutters-new-navigation-and-routing-system/index.md new file mode 100644 index 0000000000..dfb700f3fd --- /dev/null +++ b/sites/www/content/blog/learning-flutters-new-navigation-and-routing-system/index.md @@ -0,0 +1,1234 @@ +--- +title: "Learning Flutter’s new Navigation and Routing system" +description: "This article explains how Flutter’s new Navigator and Router API works. If you follow Flutter’s open design docs, you might have seen…" +publishDate: 2020-09-30 +author: johnpryan +image: images/1PYHrYurwAGyQC8vsnAaWiA.webp +category: tutorial +layout: blog +--- + +## Learning Flutter’s new navigation and routing system + +*Note: The sample code in this article is not null-safe and not compatible with Dart 3. For more information on the latest best-practices for navigation and routing in Flutter, go to the [Navigation and routing](https://docs.flutter.dev/ui/navigation) page on docs.flutter.dev.* + +This article explains how Flutter’s new `Navigator` and `Router` API works. If you follow Flutter’s open [design docs](https://flutter.dev/docs/resources/design-docs), you might have seen these new features referred to as the [Router](https://docs.google.com/document/d/1Q0jx0l4-xymph9O6zLaOY4d_f7YFpNWX_eGbzYxr9wY/edit#heading=h.l6kdsrb6j9id) widget. We’ll explore how these APIs enable more fine-tuned control over the screens in your app and how you can use it to parse routes. + +These new APIs are *not* breaking changes, they simply add a new [*declarative* API](https://flutter.dev/docs/get-started/flutter-for/declarative). Before Navigator 2.0, it was [difficult to push or pop multiple pages](https://github.com/flutter/flutter/issues/12146), or remove a page underneath the current one. However, if you are happy with how the `Navigator` works today, you can keep using it in the same (imperative) way. + +The `Router` provides the ability to handle routes from the underlying platform and display the appropriate pages. In this article, the `Router` is configured to parse the browser URL to display the appropriate page. + +This article helps you choose which `Navigator` pattern works best for your app, and explains how to use Navigator 2.0 to parse browser URLs and take full control over the stack of pages that are active. The exercise in this article shows how to build an app that handles incoming routes from the platform and manages the pages of your app. The following GIF shows the example app in action: + +<DashImage figure src="images/17-wvbHmckKFVagnUwsQI2g.webp" /> + + +## Navigator 1.0 + +If you’re using Flutter, you’re probably using the `Navigator` and are familiar with the following concepts: + +* [**`Navigator`**](https://master-api.flutter.dev/flutter/widgets/Navigator-class.html) — a widget that manages a stack of Route objects. + +* [**`Route`**](https://master-api.flutter.dev/flutter/widgets/Route-class.html) — an object managed by a `Navigator` that represents a screen, typically implemented by classes like `MaterialPageRoute`. + +Before Navigator 2.0, `Routes` were pushed and popped onto the `Navigator`’s stack with either *named routes* or *anonymous routes.* The next sections are a brief recap of these two approaches. + +### Anonymous routes + +Most mobile apps display screens on top of each other, like a stack. In Flutter, this is easy to achieve by using the [`Navigator`](https://api.flutter.dev/flutter/widgets/Navigator-class.html). + +`MaterialApp` and `CupertinoApp` already use a `Navigator` under the hood. You can access the navigator using [`Navigator.of()`](https://api.flutter.dev/flutter/widgets/Navigator/of.html) or display a new screen using [`Navigator.push()`](https://api.flutter.dev/flutter/widgets/Navigator/push.html), and return to the previous screen with [`Navigator.pop()`](https://api.flutter.dev/flutter/services/SystemNavigator/pop.html): + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(Nav2App()); +} + +class Nav2App extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + home: HomeScreen(), + ); + } +} + +class HomeScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Center( + child: FlatButton( + child: Text('View Details'), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + return DetailScreen(); + }), + ); + }, + ), + ), + ); + } +} + +class DetailScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Center( + child: FlatButton( + child: Text('Pop!'), + onPressed: () { + Navigator.pop(context); + }, + ), + ), + ); + } +} + +``` + +When `push()` is called, the `DetailScreen` widget is placed on top of the `HomeScreen` widget like this: + +<DashImage figure src="images/1v77nG0BRIWrOghj8fCq_EA.webp" /> + + +The previous screen (`HomeScreen`) is still part of the widget tree, so any `State` object associated with it stays around while `DetailScreen` is visible. + +### Named routes + +Flutter also supports named routes, which are defined in the `routes` parameter on `MaterialApp` or `CupertinoApp`: + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(Nav2App()); +} + +class Nav2App extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + routes: { + '/': (context) => HomeScreen(), + '/details': (context) => DetailScreen(), + }, + ); + } +} + +class HomeScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Center( + child: FlatButton( + child: Text('View Details'), + onPressed: () { + Navigator.pushNamed( + context, + '/details', + ); + }, + ), + ), + ); + } +} + +class DetailScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Center( + child: FlatButton( + child: Text('Pop!'), + onPressed: () { + Navigator.pop(context); + }, + ), + ), + ); + } +} + +``` + +These routes must be predefined. Although you can [pass arguments to a named route](https://flutter.dev/docs/cookbook/navigation/navigate-with-arguments), you can’t parse arguments from the route itself. For example, if the app is run on the web, you can’t parse the ID from a route like `/details/:id`. + +### Advanced named routes with onGenerateRoute + +A more flexible way to handle named routes is by using `onGenerateRoute`. This API gives you the ability to handle all paths: + +```dart +onGenerateRoute: (settings) { + // Handle '/' + if (settings.name == '/') { + return MaterialPageRoute(builder: (context) => HomeScreen()); + } + + // Handle '/details/:id' + var uri = Uri.parse(settings.name); + if (uri.pathSegments.length == 2 && + uri.pathSegments.first == 'details') { + var id = uri.pathSegments[1]; + return MaterialPageRoute(builder: (context) => DetailScreen(id: id)); + } + + return MaterialPageRoute(builder: (context) => UnknownScreen()); +}, +``` + +Here’s the complete example: + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(Nav2App()); +} + +class Nav2App extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + onGenerateRoute: (settings) { + // Handle '/' + if (settings.name == '/') { + return MaterialPageRoute(builder: (context) => HomeScreen()); + } + + // Handle '/details/:id' + var uri = Uri.parse(settings.name); + if (uri.pathSegments.length == 2 && + uri.pathSegments.first == 'details') { + var id = uri.pathSegments[1]; + return MaterialPageRoute(builder: (context) => DetailScreen(id: id)); + } + + return MaterialPageRoute(builder: (context) => UnknownScreen()); + }, + ); + } +} + +class HomeScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Center( + child: FlatButton( + child: Text('View Details'), + onPressed: () { + Navigator.pushNamed( + context, + '/details/1', + ); + }, + ), + ), + ); + } +} + +class DetailScreen extends StatelessWidget { + String id; + + DetailScreen({ + this.id, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Viewing details for item $id'), + FlatButton( + child: Text('Pop!'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ), + ), + ); + } +} + +class UnknownScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Center( + child: Text('404!'), + ), + ); + } +} + +``` + +Here, `settings` is an instance of [`RouteSettings`](https://api.flutter.dev/flutter/widgets/RouteSettings-class.html). The name and arguments fields are the values that were provided when [`Navigator.pushNamed`](https://api.flutter.dev/flutter/widgets/Navigator/pushNamed.html) was called, or what [`initialRoute`](https://api.flutter.dev/flutter/material/MaterialApp/initialRoute.html) is set to. + +## Navigator 2.0 + +The Navigator 2.0 API adds new classes to the framework in order to make the app’s screens a function of the app state and to provide the ability to parse routes from the underlying platform (like web URLs). Here’s an overview of what’s new: + +* [**`Page`**](https://master-api.flutter.dev/flutter/widgets/Page-class.html) — an immutable object used to set the navigator’s history stack. + +* [**`Router`**](https://master-api.flutter.dev/flutter/widgets/Router-class.html) — configures the list of pages to be displayed by the Navigator. Usually this list of pages changes based on the underlying platform, or on the state of the app changing. + +* [**`RouteInformationParser`**](https://master-api.flutter.dev/flutter/widgets/Router/routeInformationParser.html), which takes the [`RouteInformation`](https://master-api.flutter.dev/flutter/widgets/RouteInformation-class.html) from [`RouteInformationProvider`](https://master-api.flutter.dev/flutter/widgets/RouteInformationProvider-class.html) and parses it into a user-defined data type. + +* [**`RouterDelegate`**](https://master-api.flutter.dev/flutter/widgets/RouterDelegate-class.html) — defines app-specific behavior of how the `Router` learns about changes in app state and how it responds to them. Its job is to listen to the `RouteInformationParser` and the app state and build the `Navigator` with the current list of `Pages`. + +* [**`BackButtonDispatcher`**](https://master-api.flutter.dev/flutter/widgets/BackButtonDispatcher-class.html) — reports back button presses to the `Router`. + +The following diagram shows how the `RouterDelegate` interacts with the `Router`, `RouteInformationParser`, and the app’s state: + +<DashImage figure src="images/1hNt4Bc8FZBp_Gqh7iED3FA.webp" /> + + +Here’s an example of how these pieces interact: + +1. When the platform emits a new route (for example, “books/2”) , the `RouteInformationParser` converts it into an abstract data type `T` that you define in your app (for example, a class called `BooksRoutePath`). + +1. `RouterDelegate`’s `setNewRoutePath` method is called with this data type, and must update the application state to reflect the change (for example, by setting the `selectedBookId`) and call `notifyListeners.` + +1. When `notifyListeners` is called, it tells the `Router` to rebuild the `RouterDelegate` (using its `build()` method) + +1. `RouterDelegate.build()` returns a new `Navigator`, whose pages now reflect the change to the app state (for example, the `selectedBookId`). + +## Navigator 2.0 exercise + +This section leads you through an exercise using the Navigator 2.0 API. We’ll end up with an app that can stay in sync with the URL bar, and handle back button presses from the app and the browser, as shown in the following GIF: + +<DashImage figure src="images/1PYHrYurwAGyQC8vsnAaWiA.webp" /> + + +To follow along, [switch to the master channel](https://flutter.dev/docs/development/tools/sdk/upgrading#switching-flutter-channels), [create a new Flutter project with web support](https://flutter.dev/docs/get-started/web#create-a-new-project-with-web-support), and replace the contents of `lib/main.dart` with the following: + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(BooksApp()); +} + +class Book { + final String title; + final String author; + + Book(this.title, this.author); +} + +class BooksApp extends StatefulWidget { + @override + State<StatefulWidget> createState() => _BooksAppState(); +} + +class _BooksAppState extends State<BooksApp> { + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Books App', + home: Navigator( + pages: [ + MaterialPage( + key: ValueKey('BooksListPage'), + child: Scaffold(), + ) + ], + onPopPage: (route, result) => route.didPop(result), + ), + ); + } +} + +``` + +## Pages + +The Navigator has a new `pages` argument in its constructor. If the list of `Page` objects changes, `Navigator` updates the stack of routes to match. To see how this works, we’ll build an app that displays a list of books. + +In `_BooksAppState`, keep two pieces of state: a list of books and the selected book: + +```dart +class _BooksAppState extends State<BooksApp> { + // New: + Book _selectedBook; + bool show404 = false; + List<Book> books = [ + Book('Left Hand of Darkness', 'Ursula K. Le Guin'), + Book('Too Like the Lightning', 'Ada Palmer'), + Book('Kindred', 'Octavia E. Butler'), + ]; + + // ... +``` + +Then in `_BooksAppState`, return a `Navigator` with a list of `Page` objects: + +```dart +@override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Books App', + home: Navigator( + pages: [ + MaterialPage( + key: ValueKey('BooksListPage'), + child: BooksListScreen( + books: books, + onTapped: _handleBookTapped, + ), + ), + ], + ), + ); + } +void _handleBookTapped(Book book) { + setState(() { + _selectedBook = book; + }); + } +// ... +class BooksListScreen extends StatelessWidget { + final List<Book> books; + final ValueChanged<Book> onTapped; +BooksListScreen({ + @required this.books, + @required this.onTapped, + }); +@override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: ListView( + children: [ + for (var book in books) + ListTile( + title: Text(book.title), + subtitle: Text(book.author), + onTap: () => onTapped(book), + ) + ], + ), + ); + } +} +``` + +Since this app has two screens, a list of books and a screen showing the details, add a second (detail) page if a book is selected (using [`collection if`](https://dart.dev/guides/language/language-tour#collection-operators)): + +```dart +pages: [ + MaterialPage( + key: ValueKey('BooksListPage'), + child: BooksListScreen( + books: books, + onTapped: _handleBookTapped, + ), + ), +// New: + if (show404) + MaterialPage(key: ValueKey('UnknownPage'), child: UnknownScreen()) + else if (_selectedBook != null) + MaterialPage( + key: ValueKey(_selectedBook), + child: BookDetailsScreen(book: _selectedBook)) +], +``` + +Note that the `key` for the page is defined by the value of the `book` object. This tells the `Navigator` that this `MaterialPage` object is different from another when the `Book` object is different. Without a unique key, the framework can’t determine when to show a transition animation between different `Pages`. + +**Note**: If you prefer, you can also extend `Page` to customize the behavior. For example, this page adds a custom transition animation: + +```dart +class BookDetailsPage extends Page { + final Book book; + + BookDetailsPage({ + this.book, + }) : super(key: ValueKey(book)); + + Route createRoute(BuildContext context) { + return PageRouteBuilder( + settings: this, + pageBuilder: (context, animation, animation2) { + final tween = Tween(begin: Offset(0.0, 1.0), end: Offset.zero); + final curveTween = CurveTween(curve: Curves.easeInOut); + return SlideTransition( + position: animation.drive(curveTween).drive(tween), + child: BookDetailsScreen( + key: ValueKey(book), + book: book, + ), + ); + }, + ); + } +} +``` + +Finally, it’s an error to provide a [`pages`](https://api.flutter.dev/flutter/widgets/Navigator/pages.html) argument without also providing an [`onPopPage`](https://api.flutter.dev/flutter/widgets/Navigator/onPopPage.html) callback. This function is called whenever `Navigator.pop()` is called. It should be used to update the state (that determines the list of pages), and it must call `didPop` on the route to determine if the pop succeeded: + +```dart +onPopPage: (route, result) { + if (!route.didPop(result)) { + return false; + } + + // Update the list of pages by setting _selectedBook to null + setState(() { + _selectedBook = null; + }); + + return true; +}, + +``` + +It’s important to check whether `didPop` fails before updating the app state. + +Using `setState` notifies the framework to call the `build()` method, which returns a list with a single page when `_selectedBook` is null. + +Here’s the full example: + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(BooksApp()); +} + +class Book { + final String title; + final String author; + + Book(this.title, this.author); +} + +class BooksApp extends StatefulWidget { + @override + State<StatefulWidget> createState() => _BooksAppState(); +} + +class _BooksAppState extends State<BooksApp> { + Book _selectedBook; + + List<Book> books = [ + Book('Left Hand of Darkness', 'Ursula K. Le Guin'), + Book('Too Like the Lightning', 'Ada Palmer'), + Book('Kindred', 'Octavia E. Butler'), + ]; + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Books App', + home: Navigator( + pages: [ + MaterialPage( + key: ValueKey('BooksListPage'), + child: BooksListScreen( + books: books, + onTapped: _handleBookTapped, + ), + ), + if (_selectedBook != null) BookDetailsPage(book: _selectedBook) + ], + onPopPage: (route, result) { + if (!route.didPop(result)) { + return false; + } + + // Update the list of pages by setting _selectedBook to null + setState(() { + _selectedBook = null; + }); + + return true; + }, + ), + ); + } + + void _handleBookTapped(Book book) { + setState(() { + _selectedBook = book; + }); + } +} + +class BookDetailsPage extends Page { + final Book book; + + BookDetailsPage({ + this.book, + }) : super(key: ValueKey(book)); + + Route createRoute(BuildContext context) { + return MaterialPageRoute( + settings: this, + builder: (BuildContext context) { + return BookDetailsScreen(book: book); + }, + ); + } +} + +class BooksListScreen extends StatelessWidget { + final List<Book> books; + final ValueChanged<Book> onTapped; + + BooksListScreen({ + @required this.books, + @required this.onTapped, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: ListView( + children: [ + for (var book in books) + ListTile( + title: Text(book.title), + subtitle: Text(book.author), + onTap: () => onTapped(book), + ) + ], + ), + ); + } +} + +class BookDetailsScreen extends StatelessWidget { + final Book book; + + BookDetailsScreen({ + @required this.book, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (book != null) ...[ + Text(book.title, style: Theme.of(context).textTheme.headline6), + Text(book.author, style: Theme.of(context).textTheme.subtitle1), + ], + ], + ), + ), + ); + } +} + +``` + +As it stands, this app only enables us to define the stack of pages in a declarative way. We aren’t able to handle the platform’s back button, and the browser’s URL doesn’t change as we navigate. + +## Router + +So far, the app can show different pages, but it can’t handle routes from the underlying platform, for example if the user updates the URL in the browser. + +This section shows how to implement the `RouteInformationParser`, `RouterDelegate`, and update the app state. Once set up, the app stays in sync with the browser’s URL. + +### **Data types** + +The `RouteInformationParser` parses the route information into a user-defined data type, so we’ll define that first: + +```dart +class BookRoutePath { + final int id; + final bool isUnknown; + + BookRoutePath.home() + : id = null, + isUnknown = false; + + BookRoutePath.details(this.id) : isUnknown = false; + + BookRoutePath.unknown() + : id = null, + isUnknown = true; + + bool get isHomePage => id == null; + + bool get isDetailsPage => id != null; +} + +``` + +In this app, all of the routes in the app can be represented using a single class. Instead, you might choose to use different classes that implement a superclass, or manage the route information in another way. + +### **RouterDelegate** + +Next, add a class that extends `RouterDelegate`: + +```dart +class BookRouterDelegate extends RouterDelegate<BookRoutePath> + with ChangeNotifier, PopNavigatorRouterDelegateMixin<BookRoutePath> { + @override + Widget build(BuildContext context) { + // TODO + throw UnimplementedError(); + } + + @override + // TODO + GlobalKey<NavigatorState> get navigatorKey => throw UnimplementedError(); + + @override + Future<void> setNewRoutePath(BookRoutePath configuration) { + // TODO + throw UnimplementedError(); + } +} + +``` + +The generic type defined on `RouterDelegate` is `BookRoutePath`, which contains all the state needed to decide which pages to show. + +We’ll need to move some logic from `_BooksAppState` to `BookRouterDelegate`, and create a `GlobalKey`. In this example, the app state is stored directly on the `RouterDelegate`, but could also be separated into another class. + +```dart +class BookRouterDelegate extends RouterDelegate<BookRoutePath> + with ChangeNotifier, PopNavigatorRouterDelegateMixin<BookRoutePath> { + final GlobalKey<NavigatorState> navigatorKey; + + Book _selectedBook; + bool show404 = false; + + List<Book> books = [ + Book('Left Hand of Darkness', 'Ursula K. Le Guin'), + Book('Too Like the Lightning', 'Ada Palmer'), + Book('Kindred', 'Octavia E. Butler'), + ]; + + BookRouterDelegate() : navigatorKey = GlobalKey<NavigatorState>(); + // ... +``` + +In order to show the correct path in the URL, we need to return a `BookRoutePath` based on the current state of the app: + +```dart + BookRoutePath get currentConfiguration { + if (show404) { + return BookRoutePath.unknown(); + } + + return _selectedBook == null + ? BookRoutePath.home() + : BookRoutePath.details(books.indexOf(_selectedBook)); + } +``` + +Next, the `build` method in a `RouterDelegate` needs to return a `Navigator`: + +```dart +@override +Widget build(BuildContext context) { + return Navigator( + key: navigatorKey, + pages: [ + MaterialPage( + key: ValueKey('BooksListPage'), + child: BooksListScreen( + books: books, + onTapped: _handleBookTapped, + ), + ), + if (show404) + MaterialPage(key: ValueKey('UnknownPage'), child: UnknownScreen()) + else if (_selectedBook != null) + BookDetailsPage(book: _selectedBook) + ], + onPopPage: (route, result) { + if (!route.didPop(result)) { + return false; + } + + // Update the list of pages by setting _selectedBook to null + _selectedBook = null; + show404 = false; + notifyListeners(); + + return true; + }, + ); +} + +``` + +The `onPopPage` callback now uses `notifyListeners` instead of `setState`, since this class is now a `ChangeNotifier`, not a widget. When the `RouterDelegate` notifies its listeners, the `Router` widget is likewise notified that the `RouterDelegate's` `currentConfiguration` has changed and that its `build` method needs to be called again to build a new `Navigator`. + +The `_handleBookTapped` method also needs to use `notifyListeners` instead of `setState`: + +```dart + void _handleBookTapped(Book book) { + _selectedBook = book; + notifyListeners(); + } +``` + +When a new route has been pushed to the application, `Router` calls `setNewRoutePath`, which gives our app the opportunity to update the app state based on the changes to the route: + +```dart + @override + Future<void> setNewRoutePath(BookRoutePath path) async { + if (path.isUnknown) { + _selectedBook = null; + show404 = true; + return; + } + + if (path.isDetailsPage) { + if (path.id < 0 || path.id > books.length - 1) { + show404 = true; + return; + } + + _selectedBook = books[path.id]; + } else { + _selectedBook = null; + } + + show404 = false; + } +``` + +### **RouteInformationParser** + +The `RouteInformationParser` provides a hook to parse incoming routes (`RouteInformation`) and convert it into a user defined type (`BookRoutePath`). Use the `Uri` class to take care of the parsing: + +```dart +class BookRouteInformationParser extends RouteInformationParser<BookRoutePath> { + @override + Future<BookRoutePath> parseRouteInformation( + RouteInformation routeInformation) async { + final uri = Uri.parse(routeInformation.location); + // Handle '/' + if (uri.pathSegments.length == 0) { + return BookRoutePath.home(); + } + + // Handle '/book/:id' + if (uri.pathSegments.length == 2) { + if (uri.pathSegments[0] != 'book') return BookRoutePath.unknown(); + var remaining = uri.pathSegments[1]; + var id = int.tryParse(remaining); + if (id == null) return BookRoutePath.unknown(); + return BookRoutePath.details(id); + } + + // Handle unknown routes + return BookRoutePath.unknown(); + } + + @override + RouteInformation restoreRouteInformation(BookRoutePath path) { + if (path.isUnknown) { + return RouteInformation(location: '/404'); + } + if (path.isHomePage) { + return RouteInformation(location: '/'); + } + if (path.isDetailsPage) { + return RouteInformation(location: '/book/${path.id}'); + } + return null; + } +} + +``` + +This implementation is specific to this app, not a general route parsing solution. More on that later. + +To use these new classes, we use the new `MaterialApp.router` constructor and pass in our custom implementations: + +```dart + return MaterialApp.router( + title: 'Books App', + routerDelegate: _routerDelegate, + routeInformationParser: _routeInformationParser, + ); +``` + +Here’s the complete example: + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(BooksApp()); +} + +class Book { + final String title; + final String author; + + Book(this.title, this.author); +} + +class BooksApp extends StatefulWidget { + @override + State<StatefulWidget> createState() => _BooksAppState(); +} + +class _BooksAppState extends State<BooksApp> { + BookRouterDelegate _routerDelegate = BookRouterDelegate(); + BookRouteInformationParser _routeInformationParser = + BookRouteInformationParser(); + + @override + Widget build(BuildContext context) { + return MaterialApp.router( + title: 'Books App', + routerDelegate: _routerDelegate, + routeInformationParser: _routeInformationParser, + ); + } +} + +class BookRouteInformationParser extends RouteInformationParser<BookRoutePath> { + @override + Future<BookRoutePath> parseRouteInformation( + RouteInformation routeInformation) async { + final uri = Uri.parse(routeInformation.location); + // Handle '/' + if (uri.pathSegments.length == 0) { + return BookRoutePath.home(); + } + + // Handle '/book/:id' + if (uri.pathSegments.length == 2) { + if (uri.pathSegments[0] != 'book') return BookRoutePath.unknown(); + var remaining = uri.pathSegments[1]; + var id = int.tryParse(remaining); + if (id == null) return BookRoutePath.unknown(); + return BookRoutePath.details(id); + } + + // Handle unknown routes + return BookRoutePath.unknown(); + } + + @override + RouteInformation restoreRouteInformation(BookRoutePath path) { + if (path.isUnknown) { + return RouteInformation(location: '/404'); + } + if (path.isHomePage) { + return RouteInformation(location: '/'); + } + if (path.isDetailsPage) { + return RouteInformation(location: '/book/${path.id}'); + } + return null; + } +} + +class BookRouterDelegate extends RouterDelegate<BookRoutePath> + with ChangeNotifier, PopNavigatorRouterDelegateMixin<BookRoutePath> { + final GlobalKey<NavigatorState> navigatorKey; + + Book _selectedBook; + bool show404 = false; + + List<Book> books = [ + Book('Left Hand of Darkness', 'Ursula K. Le Guin'), + Book('Too Like the Lightning', 'Ada Palmer'), + Book('Kindred', 'Octavia E. Butler'), + ]; + + BookRouterDelegate() : navigatorKey = GlobalKey<NavigatorState>(); + + BookRoutePath get currentConfiguration { + if (show404) { + return BookRoutePath.unknown(); + } + return _selectedBook == null + ? BookRoutePath.home() + : BookRoutePath.details(books.indexOf(_selectedBook)); + } + + @override + Widget build(BuildContext context) { + return Navigator( + key: navigatorKey, + pages: [ + MaterialPage( + key: ValueKey('BooksListPage'), + child: BooksListScreen( + books: books, + onTapped: _handleBookTapped, + ), + ), + if (show404) + MaterialPage(key: ValueKey('UnknownPage'), child: UnknownScreen()) + else if (_selectedBook != null) + BookDetailsPage(book: _selectedBook) + ], + onPopPage: (route, result) { + if (!route.didPop(result)) { + return false; + } + + // Update the list of pages by setting _selectedBook to null + _selectedBook = null; + show404 = false; + notifyListeners(); + + return true; + }, + ); + } + + @override + Future<void> setNewRoutePath(BookRoutePath path) async { + if (path.isUnknown) { + _selectedBook = null; + show404 = true; + return; + } + + if (path.isDetailsPage) { + if (path.id < 0 || path.id > books.length - 1) { + show404 = true; + return; + } + + _selectedBook = books[path.id]; + } else { + _selectedBook = null; + } + + show404 = false; + } + + void _handleBookTapped(Book book) { + _selectedBook = book; + notifyListeners(); + } +} + +class BookDetailsPage extends Page { + final Book book; + + BookDetailsPage({ + this.book, + }) : super(key: ValueKey(book)); + + Route createRoute(BuildContext context) { + return MaterialPageRoute( + settings: this, + builder: (BuildContext context) { + return BookDetailsScreen(book: book); + }, + ); + } +} + +class BookRoutePath { + final int id; + final bool isUnknown; + + BookRoutePath.home() + : id = null, + isUnknown = false; + + BookRoutePath.details(this.id) : isUnknown = false; + + BookRoutePath.unknown() + : id = null, + isUnknown = true; + + bool get isHomePage => id == null; + + bool get isDetailsPage => id != null; +} + +class BooksListScreen extends StatelessWidget { + final List<Book> books; + final ValueChanged<Book> onTapped; + + BooksListScreen({ + @required this.books, + @required this.onTapped, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: ListView( + children: [ + for (var book in books) + ListTile( + title: Text(book.title), + subtitle: Text(book.author), + onTap: () => onTapped(book), + ) + ], + ), + ); + } +} + +class BookDetailsScreen extends StatelessWidget { + final Book book; + + BookDetailsScreen({ + @required this.book, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (book != null) ...[ + Text(book.title, style: Theme.of(context).textTheme.headline6), + Text(book.author, style: Theme.of(context).textTheme.subtitle1), + ], + ], + ), + ), + ); + } +} + +class UnknownScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Center( + child: Text('404!'), + ), + ); + } +} + +``` + +Running this sample in Chrome now shows the routes as they are being navigated, and navigates to the correct page when the URL is manually edited. + +### **TransitionDelegate** + +You can provide a custom implementation of `TransitionDelegate` that customizes how routes appear on (or are removed from) the screen when the list of pages changes. If you need to customize this, read on, but if you are happy with the default behavior you can skip this section. + +Provide a custom `TransitionDelegate` to a `Navigator` that defines the desired behavior: + +```dart +// New: +TransitionDelegate transitionDelegate = NoAnimationTransitionDelegate(); + + child: Navigator( + key: navigatorKey, + // New: + transitionDelegate: transitionDelegate, +``` + +For example, the following implementation disables all transition animations: + +```dart +class NoAnimationTransitionDelegate extends TransitionDelegate<void> { + @override + Iterable<RouteTransitionRecord> resolve({ + List<RouteTransitionRecord> newPageRouteHistory, + Map<RouteTransitionRecord, RouteTransitionRecord> + locationToExitingPageRoute, + Map<RouteTransitionRecord, List<RouteTransitionRecord>> + pageRouteToPagelessRoutes, + }) { + final results = <RouteTransitionRecord>[]; + + for (final pageRoute in newPageRouteHistory) { + if (pageRoute.isWaitingForEnteringDecision) { + pageRoute.markForAdd(); + } + results.add(pageRoute); + } + + for (final exitingPageRoute in locationToExitingPageRoute.values) { + if (exitingPageRoute.isWaitingForExitingDecision) { + exitingPageRoute.markForRemove(); + final pagelessRoutes = pageRouteToPagelessRoutes[exitingPageRoute]; + if (pagelessRoutes != null) { + for (final pagelessRoute in pagelessRoutes) { + pagelessRoute.markForRemove(); + } + } + } + + results.add(exitingPageRoute); + } + return results; + } +} + +``` + +This custom implementation overrides `resolve(`), which is in charge of marking the various routes as either pushed, popped, added, completed, or removed: + +* `markForPush` — displays the route with an animated transition + +* `markForAdd` — displays the route *without* an animated transition + +* `markForPop` — removes the route with an animated transition and completes it with a result. “Completing” in this context means that the `result` object is passed to the `onPopPage` callback on `AppRouterDelegate`. + +* `markForComplete` — removes the route without a transition and completes it with a `result` + +* `markForRemove` — removes the route with no animated transition and without completing. + +This class only affects the *declarative* API, which is why the **back** button still displays a transition animation. + +**How this example works:** This example looks at both the new routes and the routes that are exiting the screen. It goes through all of the objects in `newPageRouteHistory` and marks them to be added without a transition animation using `markForAdd`. Next, it loops through values of the `locationToExitingPageRoute` map. If it finds a route marked as `isWaitingForExitingDecision`, then it calls `markForRemove` to indicate that the route should be removed without a transition and without completing. + +[Here’s the full sample(Gist)](http://gist.github.com/5ce79aee5b5f83cfababa97c9cf0a204). + +### Nested routers + +This larger demo shows how to add a `Router` within another `Router`. Many apps require routes for the destinations in a `BottomAppBar`, and routes for a stack of views above it, which [requires two Navigators](https://medium.com/flutter/getting-to-the-bottom-of-navigation-in-flutter-b3e440b9386). To do this, the app uses an application state object to store app-specific navigation state (the selected menu index and the selected `Book` object). This example also shows how to configure which `Router` handles the back button. + +[Nested router sample(Gist)](http://gist.github.com/bbca91e23bbb4d39247fa922533be7c9) + +## What’s next + +This article explored how to use these APIs for a specific app, but could also be used to build a higher-level API package. We hope that you’ll join us in exploring what a higher-level API built on top of these features can do for users. \ No newline at end of file diff --git a/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/03DmGJe1yD4eKdnfs.webp b/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/03DmGJe1yD4eKdnfs.webp new file mode 100644 index 0000000000..23602bbd88 Binary files /dev/null and b/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/03DmGJe1yD4eKdnfs.webp differ diff --git a/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/0IWHCMv72Wj3NVy4f.webp b/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/0IWHCMv72Wj3NVy4f.webp new file mode 100644 index 0000000000..10cdad701d Binary files /dev/null and b/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/0IWHCMv72Wj3NVy4f.webp differ diff --git a/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/0PY9bV7cnRCdxV2jU.webp b/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/0PY9bV7cnRCdxV2jU.webp new file mode 100644 index 0000000000..f0d2db7981 Binary files /dev/null and b/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/0PY9bV7cnRCdxV2jU.webp differ diff --git a/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/0Vd72-ykzg8BadAGp.webp b/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/0Vd72-ykzg8BadAGp.webp new file mode 100644 index 0000000000..a4539017b6 Binary files /dev/null and b/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/0Vd72-ykzg8BadAGp.webp differ diff --git a/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/0iomk4DoJwIXI44jX.webp b/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/0iomk4DoJwIXI44jX.webp new file mode 100644 index 0000000000..3af27b3b08 Binary files /dev/null and b/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/0iomk4DoJwIXI44jX.webp differ diff --git a/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/0vtUIXYkW6Bt9fvaI.webp b/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/0vtUIXYkW6Bt9fvaI.webp new file mode 100644 index 0000000000..e318bc172f Binary files /dev/null and b/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/0vtUIXYkW6Bt9fvaI.webp differ diff --git a/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/1ZACl0_Sm8xPguexZrc4vKA.webp b/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/1ZACl0_Sm8xPguexZrc4vKA.webp new file mode 100644 index 0000000000..2f2f1d3ff3 Binary files /dev/null and b/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/1ZACl0_Sm8xPguexZrc4vKA.webp differ diff --git a/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/1r2pc1eaUCaAe21EK-OlGpg.webp b/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/1r2pc1eaUCaAe21EK-OlGpg.webp new file mode 100644 index 0000000000..d41ffc9e82 Binary files /dev/null and b/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/images/1r2pc1eaUCaAe21EK-OlGpg.webp differ diff --git a/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/index.md b/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/index.md new file mode 100644 index 0000000000..0fb0e4e00c --- /dev/null +++ b/sites/www/content/blog/major-steps-this-year-on-the-journey-to-multiplatform-development/index.md @@ -0,0 +1,107 @@ +--- +title: "Major steps this year on the journey to multiplatform development" +description: "Today, we’re happy to announce two new SDK releases — Dart 3.2 and Flutter 3.16 — and a major refresh of the Flutter Casual Games Toolkit." +publishDate: 2023-11-15 +author: mit-mit +image: images/0iomk4DoJwIXI44jX.webp +category: announcements +layout: blog +--- + +Today, we’re happy to announce two new SDK releases — Dart 3.2 and Flutter 3.16 — and a major refresh of the Flutter Casual Games Toolkit. + +<DashImage figure src="images/1r2pc1eaUCaAe21EK-OlGpg.webp" alt="Flutter 3.16" caption="Flutter 3.16" /> + + +## The Flutter and Dart value proposition + +At the beginning of the year at Flutter Forward in Nairobi, Kenya, we introduced new roadmaps for Flutter and Dart. Both roadmaps focused on four high-level themes: breakthrough graphics experiences, seamless integration with web and mobile platforms, early to new and emerging architectures, and continued focus on developer experience. + +<DashImage figure src="images/0iomk4DoJwIXI44jX.webp" alt="Four high-level themes from Flutter Forward" caption="Four high-level themes from Flutter Forward" /> + + +In May at Google I/O, we [announced](https://www.youtube.com/watch?v=yRlwOdCK7Ho&t=11s) significant progress towards that vision, including (but not limited to) improving rendering performance on iOS (with Impeller), supporting element embedding on the web, and the finalization of the journey towards 100% null safety in Dart 3. + +As we approach the end of the year, here’s another update on the progress made since then. + +## Delivering user value with Flutter and Dart + +With every new feature, improvement, or bug fix, the overall goal remains unchanged: to provide a strong language and framework pairing, enabling you to build beautiful, rich, and fast apps. And you can do so from a single, shared code base — delivering to app users on mobile, web, and desktop — without having to fragment your product roadmap. We also remain committed to open source, with no licensing costs, and seek to develop rich ecosystems powered by openness and collaboration. + +This goal continues to resonate with developers both inside Google, and across Flutter’s far reaching ecosystem across the globe. Lots of great Google apps built with Flutter shipped this year: + +* As we announced at Flutter Forward, the **Google Classroom** team uses Flutter for their mobile apps, which are now live on iOS and in beta on Android. They are seeing greatly [improved performance](https://www.youtube.com/watch?v=_5CIATSDSPI&t=5s), powered by the new [Impeller](https://docs.flutter.dev/perf/impeller) rendering engine. + +* **YouTube Create**, another new Flutter app released by Google, is [available in beta](https://t.co/rn0yN2Sk9X) in 8 countries. This app leverages the flexibility of the Flutter widget library to offer a complete video editing experience with filters, effects, and music & voiceover editing. + +* With **Google Earth** 10 — now powered by Flutter — users can easily create, collaborate, and organize maps and geospatial data not only on the web, but also on Android and iOS mobile and tablet devices! Check out the [Google Earth post](https://medium.com/google-earth/a-fresh-look-for-google-earth-507d218b42d3) for more details about the new design, and stay tuned for a future blog post detailing how the Earth team built their app using Flutter. + +<DashImage figure src="images/0Vd72-ykzg8BadAGp.webp" alt="*Screenshots from Classroom (left), YouTube Create (middle), and Google Earth (right)*" caption="*Screenshots from Classroom (left), YouTube Create (middle), and Google Earth (right)*" /> + + +Flutter adoption also continues to grow outside of Google, across the globe: + +* Apps that shipped this year include banking apps like [Skandia](https://flutter.dev/showcase/skandia) in Sweden and [Credit Agricole](https://flutter.dev/showcase/credit-agricole) in Poland, the US [MarketWatch](https://flutter.dev/showcase/dow-jones) Virtual Stock Exchange investment education app, the [CZ](https://www.youtube.com/watch?v=OTn8Ngp2X8Q) health insurance app in the Netherlands, and Canada’s [Kijiji](https://flutter.dev/showcase/kijiji1) classifieds marketplace app. A common theme is that these teams chose Flutter to enable a consistent feature set across Android and iOS while still delivering a rich and compelling UI experience. + +* Further east, Tencent launched [Cloud Chat](https://flutter.dev/showcase/tencent-cloud-chat), a rich new Chat SDK and UI library currently ranked number one in China’s chat service market that is expanding internationally. + +* In Brazil, the SOMA fashion group initiated a major digital refresh of their Farm brand and, in the process, realized the flexibility of Flutter enabled them to scale this to other brands in the group, too. In the US, Caribou Coffee combined Flutter with Firebase to accelerate their productivity, while Lucid Motors reported that Flutter allowed them to not only deliver two mobile apps with a small team, but also that Flutters’ web support enabled internal sharing of prototypes. + +We’re delighted to witness the evolution of these apps. Read on to see the continued progress we’re making in the core framework. + +## Breakthrough graphics performance + +We firmly believe that multiplatform development shouldn’t imply a degradation of experience for developers or end users. So we go to great lengths to deliver on this aspiration. At Flutter Forward, we introduced the Impeller engine — a complete redesign and reimplementation of the graphics rendering architecture for speed and power. In Flutter 3.10, we announced that Impeller was enabled by default on iOS. Today, in Flutter 3.16, we’re pleased to share that Impeller on Android is ready for preview feedback on the stable channel. Performance has improved dramatically over the year; compared to a year ago rasterization performance measured in the Flutter Gallery benchmark is roughly twice as fast with Impeller on Vulkan. For details on how to enable Impeller, see the [Flutter 3.16 post](https://medium.com/flutter/whats-new-in-flutter-3-16-dba6cb1015d1). + +<DashImage figure src="images/0vtUIXYkW6Bt9fvaI.webp" alt="Flutter Gallery benchmark showing a 2x+ improvement over the past year with Impeller on Vulkan" caption="Flutter Gallery benchmark showing a 2x+ improvement over the past year with Impeller on Vulkan" /> + + +### Casual Games Toolkit + +One category of apps that especially benefits from fast and powerful graphics rendering is games. In 2022, we published the initial Flutter [Casual Games Toolkit](https://docs.flutter.dev/resources/games-toolkit). Today, we’re announcing a major update featuring a selection of new templates, from card games to runner-type games, and a set of game service developer resources for topics such as leaderboards, monetization, and telemetry. For more details, check out the [Casual Games Toolkit blog post](https://medium.com/flutter/building-your-next-casual-game-with-flutter-716ef457e440). + +<DashImage figure src="images/03DmGJe1yD4eKdnfs.webp" alt="An animated gif showing multiple Flutter-based games such as Trivia Crack, Debertz, and Landover running on mobile, web and desktop devices." caption="An animated gif showing multiple Flutter-based games such as Trivia Crack, Debertz, and Landover running on mobile, web and desktop devices." /> + + +## Seamless integration + +While Flutter apps generally feature very high levels of shared code across platforms — many developers report 95–99% sharing — we still believe it’s important to support platform-specific features. On iOS, [app extensions](https://developer.apple.com/app-extensions/) offer a way for users to interact with smaller versions of your app from other apps on their phone. In our last stable release, we launched [new resources](https://codelabs.developers.google.com/flutter-home-screen-widgets#0) to help you build home and lock screen widgets for your Flutter app. In Flutter 3.16, we added support for rendering [the UI for some iOS app extensions](https://docs.flutter.dev/platform-integration/ios/app-extensions) using Flutter, for example, the [share extension](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/Share.html). Android 14 introduced a new [predictive back gesture](https://developer.android.com/guide/navigation/custom-back/predictive-back-gesture), which gives the user the ability to glimpse the system UI behind the app when using a back gesture (left swipe). Flutter 3.16 supports this gesture. + +## Continued focus on developer experience + +Modern devices come in a variety of form factors — such as phones, foldables, and tablets — which have a wide range of screen sizes, and support external input devices like keyboards and mice. To ensure that your users have a great user experience, it’s important to take this into account. We recently collaborated with the Android team on adding support for their [large screen guidelines](https://developer.android.com/docs/quality-guidelines/large-screen-app-quality). We’ve published[ learnings from adding large screen support](https://medium.com/flutter/developing-flutter-apps-for-large-screens-53b7b0e17f10), updated our reference app [Wonderous](https://wonderous.app) to support multiple screen sizes, and made several fixes in the Flutter framework to better support things like keyboard, mouse, and trackpad input. New APIs allow you to determine the display’s size and pixel ratio, and support multi-window rendering. + +Throughout the year we’ve worked on completing support for [Material 3](https://m3.material.io/get-started), the latest version of the Material Design design system. Flutter’s Material widgets now fully support Material 3 and, in Flutter 3.16, Material 3 is now the default style. We find that Material adds a fresh look to apps; the new color schemes are both aesthetically pleasing and compliant with the contrast requirements for accessibility. For a hands-on experience, check out the [Material 3 gallery](https://flutter.github.io/samples/material_3.html). + +<DashImage figure src="images/1ZACl0_Sm8xPguexZrc4vKA.webp" alt="Screenshots of the Material 3 demo" caption="Screenshots of the Material 3 demo" /> + + +Two significant improvements to developer tooling include a new sidebar for VS Code and support for adding extensions to DevTools. The sidebar enables easy discovery of core Flutter tools actions such as creating a new project, invoking hot reload, opening DevTools, or selecting a target device. DevTools extensions provide a framework that [allows you to create](https://medium.com/p/c8bc1aaf8e5f/edit) new custom tools that plug into DevTools, and offers additional framework-specific functionality. [Provider](https://pub.dev/packages/provider), [Patrol](https://pub.dev/packages/patrol), and [ServerPod](https://pub.dev/packages/serverpod) are a few examples of frameworks developing DevTools extensions. + +<DashImage figure src="images/0IWHCMv72Wj3NVy4f.webp" alt="*Screenshot of a DevTools extension for the framework Serverpod*" caption="*Screenshot of a DevTools extension for the framework Serverpod*" /> + + +We rebooted the [Flutter Favorite](https://docs.flutter.dev/packages-and-plugins/favorites) program and designated [flame](https://pub.dev/packages/flame), [flutter_animate](https://pub.dev/packages/flutter_animate), [flutter_rust_bridge](https://pub.dev/packages/flutter_rust_bridge), [riverpod](https://pub.dev/packages/riverpod), [video_player](https://pub.dev/packages/video_player), [macos_ui](https://pub.dev/packages/macos_ui) and [fpdart](https://pub.dev/packages/fpdart) as new Flutter Favorites. In August, we held a first-time virtual summit for the package ecosystem, attended by more than 50 non-Googler and Googler contributors to [pub.dev](https://pub.dev). + +We’ve also seen the arrival of several new powerful packages and plugins. A few of my personal favorites are the [CameraX support](https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax#usage) in the camera plugin, the high-performance [2D scrollable TableView](https://pub.dev/packages/two_dimensional_scrollables), and the [flutterflow_ui](https://pub.dev/packages/flutterflow_ui) package, which allows designers and developers to create UI components with drag-and-drop, and then export them to Flutter widget code ([demo video](https://www.youtube.com/watch?v=yopCfhepAsM)). Furthermore, the google_maps package now supports [cloud map-styles](https://developers.google.com/maps/documentation/cloud-customization/overview), so developers can update their styles from the Google Cloud Console, and the [home_widget](https://pub.dev/packages/home_widget) package now supports creating [interactive iOS Home Screen Widgets](https://medium.com/@ABausG/interactive-homescreen-widgets-with-flutter-using-home-widget-83cb0706a417). + +## New and emerging architectures + +### Dart interoperability + +Dart is based around a core promise of enabling multiplatform Dart code to run across a [wide range of platforms](https://dart.dev/overview#platform). However, this often needs to be complemented with the ability to interop with existing code, whether that code is from older projects or APIs available in other libraries or system APIs. To enable this, Dart offers [interop with native C APIs](https://dart.dev/guides/libraries/c-interop), and is working on expanding this to support interop with [Java + Kotlin](https://dart.dev/guides/libraries/java-interop) and [Objective C + Swift](https://dart.dev/guides/libraries/objective-c-interop). For more details, checkout today’s [Dart 3.2 blog post](https://medium.com/dartlang/dart-3-2-c8de8fe1b91f). + +### The road to WebAssembly + +Wasm (also known as [WebAssembly](https://webassembly.org/)) is an exciting new instruction format for web browsers, which provides a portable, platform-neutral, binary code format for execution in modern browsers. High-level, managed languages like Dart use garbage collection, which is being added to the WebAssembly standard. As of Chrome 119, [garbage collection support for WebAssembly](https://developer.chrome.com/blog/wasmgc/) (Wasm-GC) is enabled by default. Wasm-GC support is also coming in Firefox 120, their next stable release. + +The Dart compiler support for Wasm is almost feature complete, and Flutter web now uses a new rendering engine that enables all rendering to happen in WebAssembly code. While there’s more work to do and support is still considered experimental, we’re already able to run some apps like the [Material 3 demo](https://flutterweb-wasm.web.app/). The [Dart 3.2 blog post](https://medium.com/dartlang/dart-3-2-c8de8fe1b91f) has more details. + +## The power of a vibrant community + +Today’s blog post provides a very small sampling of the thousands of improvements made over the past few quarters. More importantly, an increasing number of these improvements are coming from contributions made by developers outside of Google — the recently published [2023 GitHub Octoverse report](https://github.blog/2023-11-08-the-state-of-open-source-and-ai/) on open source activity lists the Flutter project in the top-3 among all GitHub open source projects. + +<DashImage figure src="images/0PY9bV7cnRCdxV2jU.webp" /> + + +We’ve had a great year thanks to your incredible support. We’re grateful for the diversity, enthusiasm, and kind spirit of the Dart and Flutter communities that play a critical role in making the product successful. We can’t wait to see what you build next. Until the next update, thanks 🙏, and have fun coding! \ No newline at end of file diff --git a/sites/www/content/blog/managing-flutter-application-state-with-inheritedwidgets/images/1DsbHptClIQire31aIDtF6A.webp b/sites/www/content/blog/managing-flutter-application-state-with-inheritedwidgets/images/1DsbHptClIQire31aIDtF6A.webp new file mode 100644 index 0000000000..296b7eae20 Binary files /dev/null and b/sites/www/content/blog/managing-flutter-application-state-with-inheritedwidgets/images/1DsbHptClIQire31aIDtF6A.webp differ diff --git a/sites/www/content/blog/managing-flutter-application-state-with-inheritedwidgets/images/1IVBU5tVCV5TFUI3E5V4fEg.webp b/sites/www/content/blog/managing-flutter-application-state-with-inheritedwidgets/images/1IVBU5tVCV5TFUI3E5V4fEg.webp new file mode 100644 index 0000000000..cd29f72b8a Binary files /dev/null and b/sites/www/content/blog/managing-flutter-application-state-with-inheritedwidgets/images/1IVBU5tVCV5TFUI3E5V4fEg.webp differ diff --git a/sites/www/content/blog/managing-flutter-application-state-with-inheritedwidgets/index.md b/sites/www/content/blog/managing-flutter-application-state-with-inheritedwidgets/index.md new file mode 100644 index 0000000000..8ca91792a8 --- /dev/null +++ b/sites/www/content/blog/managing-flutter-application-state-with-inheritedwidgets/index.md @@ -0,0 +1,287 @@ +--- +title: "Managing Flutter Application State With InheritedWidgets" +description: "Everyone has heard that interactive applications can be decomposed into three parts: model, view, and controller. Anyone who has given…" +publishDate: 2018-12-20 +author: hansmuller +image: images/1IVBU5tVCV5TFUI3E5V4fEg.webp +category: tutorial +layout: blog +--- + +Everyone has heard that interactive applications can be decomposed into three parts: model, view, and controller. Anyone who has given Flutter a test drive will be familiar with its react-style support for building view/controllers with widgets and callbacks. Everyone does not seem to be so clear on how Flutter supports the “model” part of MVC. + +A Flutter application’s model represents its persistent state. Widgets provide visualizations of that state and they enable the user to modify it. A widget depends on the model when its build method retrieves values from the model or when its callbacks modify model values. If a widget depends on the model, it should be rebuilt if the model changes. Exactly how one gets that to happen is this article’s subject. + +This article reviews how Flutter’s StatefulWidget and InheritedWidget classes can be used to bind an application’s visual elements to its model. It concludes with a small but hopefully useful ModelBinding class that should be easy to drop into an application. If you’re eager to get started you can [download the ModelBinding class along with a tiny demo](https://gist.github.com/HansMuller/a3a6d520c6a24238bf1b1b9e3d473bf5) now. + +<DashImage figure src="images/1DsbHptClIQire31aIDtF6A.webp" alt="[View the complete ModelBinding<T> example](https://gist.github.com/HansMuller/a3a6d520c6a24238bf1b1b9e3d473bf5)." caption="[View the complete ModelBinding<T> example](https://gist.github.com/HansMuller/a3a6d520c6a24238bf1b1b9e3d473bf5)." /> + + +## Disclaimers, Reader Expectations + +The approach for building MVC apps outlined in this article is not the one and only way to do so. There are many ways to bind Flutter to models and if you’re planning something big, you might want to survey your options, some of which I’ve listed at the end of this article. On the other hand, even if you don’t decide to adopt the little ModelBinding class presented in the final section, you may learn something new about Flutter by reading this article. + +This isn’t an introductory Flutter article, I expect that readers will already have some experience with the Flutter API. For example, it’s assumed that: + +* You are comfortable with writing classes in Dart and understand operator == and hashcode overloading as well as generic methods. + +* You are familiar with the basic Flutter widget classes and understand how to write new ones. + +## The Application’s Model + +For the sake of the examples that follow we need a sample application model. For the sake of our short attention spans, I’ve kept this model as simple as possible. There’s just one value field here and the [actual class](https://gist.github.com/HansMuller/793b7a2b7fd35a2def4337315918a26d) includes operator == and hashCode overrides. + +```dart +class Model { + const Model({ this.value = 0 }); + final int value; + // .. operator ==, hashCode +} + +``` + +Hopefully it’s obvious how one could extend the values that the model represents. + +This is an immutable model, so changing it means replacing it. The MVC approaches shown below can be used with a mutable model, it’s just a little bit more complicated that way. + +## Binding to the Model with StatefulWidget + +This is the simplest approach to integrating a model, perfect for an afternoon app. + +A StatefulWidget is associated with a persistent State object. The State object’s build method creates the subtree contributed by the widget, just like the StatelessWidget’s build method does. The State object’s setState method causes the widget to be built again, the next time the display’s frame rate interval has elapsed. If the stateful widget’s state object owns the model, its build method configures the widgets it creates using model values and it calls setState when the model changes. + +Defining the example’s ViewController stateful widget requires a little boilerplate: + +```dart +class ViewController extends StatefulWidget { + _ViewControllerState createState() => _ViewControllerState(); +} + +class _ViewControllerState extends State<ViewController> { + // the widget's persistent state goes here + @override + Widget build(BuildContext context) { + // return a widget based on the persistent state + } +} + +``` + +In this case the widget’s persistent state is a Model instance. The build method creates a button that shows the model’s current value and replaces the model when pressed. The updateModel method resets the currentModel and calls setState to trigger a rebuild. In other words, pressing the button updates the model and then rebuilds the ViewController widget. + +```dart +Model currentModel = Model(); + +void updateModel(Model newModel) { + if (newModel != currentModel) { + setState(() { + currentModel = newModel; + }); + } +} + +@override +Widget build(BuildContext context) { + return RaisedButton( + onPressed: () { + updateModel(Model(value: currentModel.value + 1)); + }, + child: Text('Hello World ${currentModel.value}'), + ); +} + +``` + +## StatefulWidget Binding Limitations + +The approach outlined above is a strawman. It’s not ideal for a relatively large scale application: + +* When the model changes, the ViewController and the entire tree of widgets it creates are rebuilt. Not just the widgets that depend on the model, all of them. + +* If a child widget needs to lookup the model’s value, then the model must be passed to it with a constructor parameter or through a closure, as in the trivial example above. + +* If a descendant widget needs to lookup the model’s value, then the model must be passed to it through a chain of constructor parameters. + +* If a child widget wants to modify the model’s value, then it must be passed a callback provided by the ViewController. In the example, that would be the RaisedButton’s onPressed parameter. + +Hopefully it’s clear that StatefulWidgets, while well suited for creating widgets with some internal persistent state, aren’t ideal for sharing a model in a complex application. And, as software engineers, complex is our bread and butter. + +## Binding to the Model with InheritedWidget, Version 0 + +The InheritedWidget class has some special properties that make it well suited to sharing a model with a tree of widgets: + +* Given a BuildContext, finding the nearest InheritedWidget ancestor of a particular type is cheap, just a table lookup. + +* InheritedWidgets keep track of their dependents, i.e. the BuildContexts from which the InheritedWidget was accessed. When an InheritedWidget is rebuilt, all of its dependents are rebuilt as well. + +You’ve probably run into the Theme inherited widget already. The static Theme.of(context) method returns the Theme’s ThemeData and records context as a Theme dependent. If the Theme is rebuilt with a new and different ThemeData value, then all of the widgets that referred to it with Theme.of() are rebuilt automatically. + +It’s easy enough to use a custom InheritedWidget subclass in the same way, as a host for the application’s model. This InheritedWidget subclass is called ModelBinding because it connects the application’s widgets to the model. + +```dart +class ModelBinding extends InheritedWidget { + ModelBinding({ + Key key, + this.model = const Model(), + Widget child, + }) : assert(model != null), super(key: key, child: child); + + final Model model; + + @override + bool updateShouldNotify(ModelBinding oldWidget) => model != oldWidget.model; +} + +``` + +The updateShouldNotify method is called when the ModelBinding is rebuilt. If it returns true, then all of the dependent widgets are rebuilt. + +The BuildContext inheritFromWidgetOfExactType() method is used to lookup an inherited widget. Because it’s a little ugly, and for other reasons that will become apparent later, it’s usually wrapped in a static method. Adding the lookup method to the Model class makes it possible to retrieve the model from any descendant of ModelBinding with: Model.of(context): + +Now ViewController can be stateless and its reference to the model becomes: + +```dart +// Model.of(context) works from any ModelBinding descendant +Text('Hello World ${Model.of(context).value}') +``` + +Any descendant of ModelBinding can do the same thing, there’s no need to pass the Model downwards. If the model changes then dependent widgets like ViewController are rebuilt automatically. + +The ModelBinding itself must be built by a stateful widget. To change the model, that stateful widget must be rebuilt by calling its setState method. Now the top level App widget becomes stateful and a callback that updates the model must be passed along to the ViewController. + +```dart +class App extends StatefulWidget { + @override + _AppState createState() => _AppState(); +} + +class _AppState extends State<App> { + Model currentModel = Model(); + + void updateModel(Model newModel) { + if (newModel != currentModel) { + setState(() { + currentModel = newModel; + }); + } + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: ModelBinding( + model: currentModel, + child: Scaffold( + body: Center( + child: ViewController(updateModel: updateModel), + ), + ), + ), + ); + } +} + +``` + +In this case, Model is just a simple immutable value, so replacing it is as simple as assigning a new currentModel. Replacing it could be more complicated, for example if the model contained objects that required lifetime management, replacing the model might require disposing parts of the old model. + +## InheritedWidget Version 0 Limitations + +On the upside, the InheritedWidget version of our model binding makes it easy for widgets to refer to the model and it automates rebuilding those “dependent” widgets when the model changes. + +On the downside, it’s still necessary to plumb an updateModel callback down through the widget tree, to widgets that need to change the model. + +## Binding to the Model with InheritedWidget, Version 1 + +This version of the InheritedWidget approach for binding widgets to a model simplifies model updates. Now any descendant of ModelBinding can get the model as well as update it, and there’s no need to pass around a model specific callback. + +So, as before, any descendant of ModelBinding can get a model value with Model.of(context), and by doing so become an automatically rebuilt model dependent. Now any descendant of ModelBinding can update the model with Model.update(context, newModel). Which is nice. + +To enable rebuilding the model with the static Model.update() method, it’s necessary to introduce an extra stateful widget. This gets a little complicated, so if you’re wearing a hat, hold on to it. + +ModelBinding is now a stateful widget that tracks the current Model value. ModelBinding builds a private _ModelBindingScope InheritedWidget child that has a reference to the State<ModelBinding> — a _ModelBindingState; essentially to its stateful widget parent. To change ModelBinding’s current model value, rebuild the ModelBinding with setState(), which in turn rebuilds the inherited _ModelBindingScope widget. The static Model methods for getting the model or updating it, lookup _ModelBindingScope: + +```dart +static Model of(BuildContext context) { + _ModelBindingScope scope = context.inheritFromWidgetOfExactType(_ModelBindingScope); + return scope.modelBindingState.currentModel; +} + +static void update(BuildContext context, Model newModel) { + _ModelBindingScope scope = context.inheritFromWidgetOfExactType(_ModelBindingScope); + scope.modelBindingState.updateModel(newModel); +} + +``` + +Any descendant of ModelBinding can now update or change the model using these methods. The example’s button does both: + +```dart +RaisedButton( + onPressed: () { + Model model = Model.of(context); + Model.update(context, Model(value: model.value + 1)); + }, + child: Text('Hello World ${Model.of(context).value}'), +); + +``` + +## InheritedWidget Version 1 Limitations + +This version is generally useful and it weighs in at well under 100 lines of code. It can be used as is, or as a starting point for a more complex model binding system. + +There is one limitation that really needs to be eliminated: the Model data type has a private connection to the ModelBinding implementation. Apps should be able to define their model types any way they like, they shouldn’t need to extend the Model class defined here. To address that, we’ll add a type parameter. + +## Binding to the Model with InheritedWidget, Finale + +This version factors out the Model type. Now the type of the app’s model is ModelBinding’s type parameter, and ModelBinding provides the generic static functions of<T>(context) and update<T>(context). + +The implementation is a bit more complicated (again) for the type parameter, which is why it was left for the finale. On the up side, the implementation is still very small, about 64 lines of code, and using the ModelBinding widget class and its two static methods should be very straightforward. + +The application creates a MyModel type ModelBinding like this: + +```dart +ModelBinding<MyModel>( + initialState: MyModel(), + child: child, +) + +``` + +The application’s model, MyModel, is retrieved and updated with generic versions of the of and update methods like this: + +```dart +RaisedButton( + onPressed: () { + MyModel model = ModelBinding.of<MyModel>(context); + ModelBinding.update<MyModel>(context, MyModel(value: model.value + 1)); + }, + child: Text('Hello World ${ModelBinding.of<MyModel>(context).value}'), +) + +``` + +The upshot is that ModelBinding et al. can be safely factored into a tiny library, rather than being wired into the application. Only the ModelBinding<T> class needs to be exported. + +## Summary + +This article was intended to explain the basics of Flutter’s StatefulWidget and InheritedWidget classes and to show how the latter could be used to bind an application’s model to its widgets. There’s much more to share about both classes and about related classes like InheritedModel and InheritedNotifier. All of that will have to wait. + +ModelBinding<T> is a simple class based on InheritedWidget that can be used to bind an arbitrary model type of type T to an application’s widgets. Use it by [downloading the example](https://gist.github.com/HansMuller/a3a6d520c6a24238bf1b1b9e3d473bf5), and dropping its page of code into your app. And if doesn’t work for you: change it. + +<DashImage figure src="images/1IVBU5tVCV5TFUI3E5V4fEg.webp" alt="[View the complete ModelBinding<T> example](https://gist.github.com/HansMuller/a3a6d520c6a24238bf1b1b9e3d473bf5)." caption="[View the complete ModelBinding<T> example](https://gist.github.com/HansMuller/a3a6d520c6a24238bf1b1b9e3d473bf5)." /> + + +## Related Articles + +This is just a quick sampling of recent articles about this popular topic. The Flutter project maintains a more comprehensive list on the flutter.io [State Management page](https://flutter.io/docs/development/data-and-backend/state-mgmt). + +* **Redux,** [Flutter ReduxThunk, an example finally](https://medium.com/flutterpub/flutter-redux-thunk-27c2f2b80a3b) +****An architecture that factors the application into a model called the “Store” which is updated by a “Reducer” that processes “Actions” generated by the user interface. + +* **Bloc,**[ Architect your Flutter project using BLoC pattern](https://medium.com/flutterpub/architecting-your-flutter-project-bd04e144a8f1) +An architecture where the model is a “Business Logic Component” (BLoC) which exposes its state with streams and enables changes via sinks. Works nicely with Flutter’s StreamBuilder widget. + +* **ScopedModel,** [The ScopedModel package](https://pub.dartlang.org/packages/scoped_model) +ScopedModel has-a model and dependents are wrapped with a ScopedModelDescendant widget which rebuilds when the model changes. Rebuilding is handled by a callback with a model parameter. \ No newline at end of file diff --git a/sites/www/content/blog/managing-issues-in-a-large-scale-open-source-project/images/0E-lTv57-czvPhZre.webp b/sites/www/content/blog/managing-issues-in-a-large-scale-open-source-project/images/0E-lTv57-czvPhZre.webp new file mode 100644 index 0000000000..fc76fb0aee Binary files /dev/null and b/sites/www/content/blog/managing-issues-in-a-large-scale-open-source-project/images/0E-lTv57-czvPhZre.webp differ diff --git a/sites/www/content/blog/managing-issues-in-a-large-scale-open-source-project/images/0aO0bWR8VrmusQNRi.webp b/sites/www/content/blog/managing-issues-in-a-large-scale-open-source-project/images/0aO0bWR8VrmusQNRi.webp new file mode 100644 index 0000000000..9756ca6ef3 Binary files /dev/null and b/sites/www/content/blog/managing-issues-in-a-large-scale-open-source-project/images/0aO0bWR8VrmusQNRi.webp differ diff --git a/sites/www/content/blog/managing-issues-in-a-large-scale-open-source-project/images/0shcSGNkownGkrpoM.webp b/sites/www/content/blog/managing-issues-in-a-large-scale-open-source-project/images/0shcSGNkownGkrpoM.webp new file mode 100644 index 0000000000..a0620896d7 Binary files /dev/null and b/sites/www/content/blog/managing-issues-in-a-large-scale-open-source-project/images/0shcSGNkownGkrpoM.webp differ diff --git a/sites/www/content/blog/managing-issues-in-a-large-scale-open-source-project/index.md b/sites/www/content/blog/managing-issues-in-a-large-scale-open-source-project/index.md new file mode 100644 index 0000000000..69ba5cf680 --- /dev/null +++ b/sites/www/content/blog/managing-issues-in-a-large-scale-open-source-project/index.md @@ -0,0 +1,140 @@ +--- +title: "Managing issues in a large-scale open source project" +description: "How the Flutter team addresses its issue backlog" +publishDate: 2020-07-17 +author: kf6gpe +image: images/0E-lTv57-czvPhZre.webp +category: spotlight +layout: blog +--- + +We’re honored by the amount of positive feedback we get from folks using Flutter. As one of the largest active repositories on GitHub, we have a relatively unique challenge and we thought you might find it interesting to learn our philosophy and approach to how we’re dealing with issues at this scale, what changes we’re making to deal with the project growth, and some helpful tips for filing issues to maximize their value. + +Sometimes developers attempt to use the total number of public logged issues as a measure of the overall quality of the project. We think this number is poorly correlated with project health, and want to explain why, counter to expectation, a larger number of issues can be a signal of a high-quality, popular project. + +Lastly, we want to use this blog post to share some concrete changes we’ve made in recent months: more active scrubbing of our backlog, accomplishing (and holding to) zero-bug-bounce for unassigned issues, and a new prioritization scheme that better aligns with how we believe our customers think about priorities. + +## How Flutter uses GitHub + +It’s common in today’s software development world for teams to keep a backlog of issues submitted both by team members and users: this backlog is the sum total of bugs, feature requests, and technical debt facing the team. Flutter is no different: we use GitHub as our backlog. If it’s a bug, an idea for a new feature, or something in between, it’s filed as an issue in our issue database [on GitHub](https://github.com/flutter/flutter/issues). + +Each of these issues starts with you, or one of Flutter’s contributors. Contributors and users file issues about Flutter or Flutter’s API documentation in the [flutter/flutter issue tracker](https://github.com/flutter/flutter/issues), and issues about Flutter’s website ([flutter.dev](https://flutter.dev)) in the [flutter/website issue tracker](https://github.com/flutter/website/issues). + +There are three special considerations about how we manage issues on GitHub, which contributes to a large overall number: + +1. The issue repository is completely open. The core engineering and product teams, along with the entire Flutter community, files all feature enhancements, bugs, and potential future work into the open repository. + +1. We don’t aggressively close issues, even if we don’t plan to work on them in the immediate future; we want to keep the total backlog of possible work open, so that any future contributor can work on a feature they’re interested in and see the discussion that’s already transpired. + +1. We use one repository for *all* issues except for those involving the developer toolchain and website. Issues with Android, iOS, Windows, web, macOS, and Linux; framework and engine; API documentation and plugins, are all logged in the same [flutter/flutter](https://github.com/flutter/flutter) repository. + +## Flutter’s triage process + +The [flutter/flutter](https://github.com/flutter/flutter) issues are triaged by a member of the team: perhaps a volunteer, or one of the folks at [Nevercode](https://blog.codemagic.io/flutter-and-codemagic-join-forces-on-github/) who have been helping us with triage, or by a Google employee (or an employee of another company). In triage, we assign as many labels to the issue as we can. + +Some labels direct the issue for secondary triage. For example, the [engine](https://github.com/flutter/flutter/labels/engine) label directs the issue to the engine team for secondary triage. Other labels indicate additional attributes, such as whether the issue is a feature request or a possible enhancement to increase the team’s velocity (as I write this, there are [over 500 issues with that label](https://github.com/flutter/flutter/labels/team)). + +Between primary and secondary triage, we also assign a priority label for issues filed against the code base or API documentation. Similar to other bug trackers, we use a ranked priority scheme, with priorities ranging from P0 to P6: + +* The [P0](https://github.com/flutter/flutter/labels/P0) label indicates top-priority issues such as build breaks or severe performance regressions that must be addressed immediately. + +* The [P1](https://github.com/flutter/flutter/labels/P1) label indicates that the issue requires timely attention, such as breakage in a major feature used by the majority of our users, or an issue blocking a strategic partner. We aim to fix or remediate P1 bugs within the current milestone. + +* The [P2](https://github.com/flutter/flutter/labels/P2) label indicates major features or issues that affect a significant subset of our users. Like P1 issues, we aim to fix or remediate these within the current milestone. + +* The [P3](https://github.com/flutter/flutter/labels/P3) label indicates an issue currently on our timeline for an upcoming release that is at the top of our work list. Many issues we are actively working on bear this label, and most should have a milestone indicating when we think they’ll land. + +* The [P4](https://github.com/flutter/flutter/labels/P4) label indicates issues that we agree are important to work on, but not at the top of the work list. This is the default level for bugs. + +* The [P5](https://github.com/flutter/flutter/labels/P5) label indicates issues we think are valid but not critical. This is the default level for new feature requests. + +* The [P6](https://github.com/flutter/flutter/labels/P6) label indicates valid issues that are unlikely to ever be worked on, but kept open in the event that a contributor might want to tackle it. + +This schema is relatively new, replacing our three-axis system of committed milestones, `customer:` and `severe:` labels. In the process of establishing it, we auto-prioritized a large number of issues based on committed milestones and other labels. + +## Measuring quality by bug count + +Because Flutter uses GitHub as a fully open issue tracker, with both bugs and feature requests, there’s no direct correlation between issue counts and product quality. Other open source projects, such as [Chromium](https://bugs.chromium.org/p/chromium/issues/list) and [TensorFlow](https://github.com/tensorflow/tensorflow/issues), also developed fully in the open, have large issue counts as well. If anything, the large number of issues suggests that the project is active and vibrant as users engage in the product finding both bugs and things they’d like to improve. + +It’s difficult to make apples-to-apples comparisons, though. Some projects proactively close open issues they’re not going to address; others keep their issue database in secret, and others use different projects for different kinds of issues. As already noted, we track feature enhancements and bugs in the same repository; we also use one repository across tools, Android, iOS, first-party plugins, and so forth. + +A better metric to gauge the health of an open-source project is to look at the number of issues closed vs. the number of issues currently open. As of this writing (early July 2020), we have 7,757 issues open and 32,485 issues closed. The rate of closure has increased over the last year, although has flattened somewhat in recent months: + +<DashImage figure src="images/0E-lTv57-czvPhZre.webp" /> + + +The dip in December is due to the holiday season; many contributors at Google and elsewhere take a vacation in that month. Regardless, the year-over-year average is 1314 issues closed, and if you look at the six-month average, you see that it’s considerably higher, at 1604 issues closed every month. + +## Understanding our open issues + +To get a better understanding of the kinds of issues we face, the Flutter leads recently took a random sample of 1,200 open issues and retriaged them. In the process, we learned the following: + +* 32% of the issues filed came from core contributors on the project. Many of these are future work items, such as features, ways to improve the product, or improvements to our team’s tooling and infrastructure that would increase our own developer velocity. + +* The average Flutter contributor filed 7.4 bugs, while the average user of Flutter who had filed any bugs had filed 1.2 bugs. + +* Of the issues we examined, some 25% were either support requests, clearly unactionable, or otherwise no longer valid (i.e., subsequent work fixed the issue or new product directions made the concern of the issue obsolete), which we were able to close. + +We’re continuing to work with Nevercode on grooming our backlog, both with recently filed issues and older issues. They have been very helpful in several ways: + +* They actively create reproducible code for issues, helping other engineers look for ways to fix bona fide bugs in Flutter. + +* They deduplicate bugs, closing duplicates after crosslinking them. + +* They close non-reproducible bugs, as well as requests for support (which should be directed to one of the channels listed at [https://www.flutter.dev/community](https://www.flutter.dev/community)). + +* They request additional information for unclear issues, closing those that are unactionable. + +* As time permits, they help us cull our backlog of old issues, answering the same questions: Is it still an issue? Does it have a reproducible case? Is it a duplicate? + +Together, we’ve learned some interesting things about the issues people file: Over the last two months, fully 55% of the issues filed don’t meet our criteria — they’re either requests for support or don’t have reproducible cases. + +For older issues, the Nevercode team has found that fewer issues were invalid or duplicated, likely explained by survivorship bias (issues that survive previous triages are likely not invalid or duplicate issues). Nonetheless, in their sample, 18% of the issues were invalid (either support requests or unactionable), and another 15% were duplicates. The lion’s share — up to 50% — reflects issues that have been subsequently addressed, suggesting that we can do a better job closing old issues as we add functionality and fix bugs. + +## Issue prioritization + +As of this writing (early July 2020), here’s the breakdown of P0-P2 issues: + +<DashImage figure src="images/0aO0bWR8VrmusQNRi.webp" /> + + +This chart shows a healthy number of issues that we’re actually working on with urgency. As you’d expect from a popular open-source project, there are a *lot* of other pieces of feedback in our issue database: here’s the breakdown of P3-P6 issues: + +<DashImage figure src="images/0shcSGNkownGkrpoM.webp" /> + + +The team is actively working on many of the P3 issues, as well as the higher-priority P0-P2 issues. + +We still have a historical backlog of triaged unprioritized issues. Of these, we believe that most will likely fall into the P5 or P6 category, because as part of Nevercode’s triage efforts, they’ve escalated those that deserve immediate attention. + +Just because an issue makes it to secondary triage does not mean that the core engineering team has bandwidth to work on it. In secondary triage, we look at several things to determine what to do with an issue, including: + +* What other work have we committed to? + +* How severe an issue is this? + +* How many people does it affect? + +* Is this issue for a specific customer? + +* How does working on this issue affect our roadmap and architecture? + +For details on how we accomplish this, you can see our previous blog post on the topic, [“Issues, Bugs, and Backlogs](https://medium.com/flutter/https-medium-com-flutter-issues-bugs-and-backlogs-5fb3adab5e25)”. + +## How you can help + +The Flutter issue database on GitHub isn’t just the team’s issue database, it’s the issue database for our entire community, so we all need to play a part in its health. Here are some things we can all do to help keep a check on the quality of issues in the database: + +* Don’t file an issue for support requests. Support requests should be raised in one of the channels we describe at [https://flutter.dev/community](https://flutter.dev/community). We actively close requests for support raised on GitHub. + +* File issues in the appropriate database: Flutter bugs, API documentation bugs, and feature requests in the [flutter/flutter issue tracker](https://github.com/flutter/flutter/issues), and feedback about the Flutter website or codelabs in the [flutter/website issue tracker](https://github.com/flutter/website/issues). + +* When filing an issue, search to see if one already exists for the issue you’re having. If so, upvote it and comment if appropriate. The development team uses upvotes as a measure of interest when ranking issues for future work. + +* Cross-linking is good! Feel free to crosslink related issues, either when filing new issues or when upvoting an issue you’ve encountered. Like upvoting, the team uses crosslinks in their investigations, and there’s a good chance that you see a relationship that others have missed. + +* Make sure that your bug contains everything the team needs to reproduce it. A minimally reproducible case is crucial for the team to be able to quickly get to the root of a bug. Explaining why a feature is important makes it more likely that the team will prioritize the work quickly. + +* Consider helping the triage effort. See a bug you have time to reproduce? Try reproducing it on the latest stable and master, and let us know if it’s still a problem. See a related issue? Crosslink it. Want to write some code? Submit a PR with a unit test exhibiting the issue, or better yet, a unit test with a fix for the issue. We welcome help on managing issues, so if you’d like to help, see our [contributing guide](https://github.com/flutter/flutter/blob/master/CONTRIBUTING.md) for detailed information on how you can join us in the triage process. + +Speaking for those working on Flutter, we’re in awe of the support you continue to give us in our vision to build ways to bring beautiful, performant applications to your users. Thanks so much for all that you do! \ No newline at end of file diff --git a/sites/www/content/blog/managing-visibility-in-flutter/images/1q-Y9wu-ZE_4YHLYcPpjgGA.jpeg b/sites/www/content/blog/managing-visibility-in-flutter/images/1q-Y9wu-ZE_4YHLYcPpjgGA.jpeg new file mode 100644 index 0000000000..3ea4439ce4 Binary files /dev/null and b/sites/www/content/blog/managing-visibility-in-flutter/images/1q-Y9wu-ZE_4YHLYcPpjgGA.jpeg differ diff --git a/sites/www/content/blog/managing-visibility-in-flutter/index.md b/sites/www/content/blog/managing-visibility-in-flutter/index.md new file mode 100644 index 0000000000..592d2e381c --- /dev/null +++ b/sites/www/content/blog/managing-visibility-in-flutter/index.md @@ -0,0 +1,80 @@ +--- +title: "Managing visibility in Flutter" +description: "How hiding widgets can also hide inefficiency in your code" +publishDate: 2018-06-27 +author: lukeaf +image: images/1q-Y9wu-ZE_4YHLYcPpjgGA.jpeg +category: tutorial +layout: blog +--- + +<DashImage figure src="images/1q-Y9wu-ZE_4YHLYcPpjgGA.jpeg" alt="By Marian ([https://www.flickr.com/photos/lescientist/8430282209](https://www.flickr.com/photos/lescientist/8430282209)) ([CC BY-NC-SA 2.0](https://creativecommons.org/licenses/by-nc-sa/2.0/))" caption="By Marian ([https://www.flickr.com/photos/lescientist/8430282209](https://www.flickr.com/photos/lescientist/8430282209)) ([CC BY-NC-SA 2.0](https://creativecommons.org/licenses/by-nc-sa/2.0/))" /> + + +**Update**: Flutter now has a built in Visibility widget so it’s better you use that. However, it’s still a good read on widget design and implementation so give it a read and a clap! + +I thought I’d write a quick snippet / tutorial on how you can easily write a generic widget to manage visibility in your Flutter application. It’s a pretty common task and it’s something that, once you’ve written it, you’ll probably re-use across all your projects. But, before we begin, let’s talk about what visibility *means*. + +For most use cases there are going to be four specific visibility states: + +### **Visible** + +This is self explanatory. + +### Invisible + +Rendered in place. Is not visible and is not interactive. You will probably use this state in conjunction with another constrained widget (e.g: Container) because Invisible widgets will still occupy space on the screen even though they’re not interactive and not visible. For example, you may have a Container with a background that has an Image that is invisible until it loads. You don’t want the UI jumping around in this case. + +### Offscreen + +Rendered off screen. Is not visible and is not interactive. It will still incur a cost but it is generally OK because it is useful for doing widget size calculations where you don’t need to show the widget all of the time. For example, if you need to calculate the size of a widget for the end state of an animation where the widget is only visible right at the end. + +### Gone + +Your widget is not rendered at all. Ideally the widget is never included in the widget tree. It should incur a zero or close to zero cost. + +Regarding cost, you should consider the following: **It is always going to be better for your app to never include the widget in the first place**. This way Flutter doesn’t have to worry about widgets that aren’t ever going to be displayed. If it’s something that you *may* include based on some state then you can still use this approach as well but it *may* help to simplify things by using a more generic mechanism. In general, your approach will be a bit like: + +``` +List<Widget> views = []; +if (shouldBeIncluded) { + views.add(myView); +} +// use views later +``` + + +When deciding what strategy to use, ask yourself the following: + +* **Will the condition for displaying my widget never be met for certain cases?** An example is when you have a first time experience, on-boarding, etc. If you have a condition where you only show it the first time the app starts then you won’t want to include it ever again. So, this one is easy. In this case you should not include your widget in the first place. You can use a *Gone* strategy. + +* **Do I need to keep the widget in place or should the rest of the UI adapt to fill the missing space?** If you need to keep it in place and do a swap or something similar then you can use an *Invisible* strategy and toggle the visibility as needed. If you need to eliminate the “white space”, then you can use *Offscreen*. + +* **Do I need to still factor in the size of my widget even if it isn’t visible?** Use *Offscreen* but read the above point first :) + +So, keeping in mind all of this information, is it possible to develop a re-usable widget to manage visibility? Yes. **With caveats**. + +So, what does the code look like? Like this: + + +A quick note on how it works before we get into the caveats: + +* For **Visible**: ****we don’t do anything special. + +* For **Invisible**: we wrap the widget in an IgnorePointer widget and an Opacity widget with the value zero. This limits your ability to interact with the widget and hides it but *will keep it in place*. So, you will see empty space wherever the widget is inserted. + +* For **Offscreen**: we wrap the widget in an Offstage widget which will render the widget off screen so that it won’t be visible and it won’t take up any screen real estate. However, it is still being rendered so it **won’t be zero cost**. Beware. + +* For **Gone**: we provide a way for you to render a different widget (defaulting to a Container with no size). If you want to render a placeholder, an icon or something else, this is your place to do it. It still isn’t zero cost, but it shouldn’t be too awful for most cases. For **true** Gone, use the strategies described above. + +So a couple of quick caveats: + +* As mentioned above, there are real costs associated with this approach so be careful and where possible just don’t include your original widget. I sound like a broken record but you shouldn’t forget it. There is no tree shaking here. + +* Opacity widgets can be super expensive so use **Invisible** sparingly. + +Aaaand that’s it. This widget is still pretty useful but, be smart and write efficient code. Your visibility approach will/should always take into consideration your specific needs so be sure to always think about what is the best approach. + +____ + +*Luke is a mobile developer and an agency veteran. He’s a Dart + Flutter GDE and passionate about technology. If you want to hire him, drop him a line!* \ No newline at end of file diff --git a/sites/www/content/blog/material-3-for-flutter/images/06JGBM5itMcIMnvYV.webp b/sites/www/content/blog/material-3-for-flutter/images/06JGBM5itMcIMnvYV.webp new file mode 100644 index 0000000000..5d4d5e7399 Binary files /dev/null and b/sites/www/content/blog/material-3-for-flutter/images/06JGBM5itMcIMnvYV.webp differ diff --git a/sites/www/content/blog/material-3-for-flutter/images/0IM7o73HEWHQMgHUe.webp b/sites/www/content/blog/material-3-for-flutter/images/0IM7o73HEWHQMgHUe.webp new file mode 100644 index 0000000000..03ef3f056d Binary files /dev/null and b/sites/www/content/blog/material-3-for-flutter/images/0IM7o73HEWHQMgHUe.webp differ diff --git a/sites/www/content/blog/material-3-for-flutter/images/0RuCmIqSnK0G0kHmy.webp b/sites/www/content/blog/material-3-for-flutter/images/0RuCmIqSnK0G0kHmy.webp new file mode 100644 index 0000000000..3cb86f24a6 Binary files /dev/null and b/sites/www/content/blog/material-3-for-flutter/images/0RuCmIqSnK0G0kHmy.webp differ diff --git a/sites/www/content/blog/material-3-for-flutter/images/0SgE01KkHs1LzX0TN.webp b/sites/www/content/blog/material-3-for-flutter/images/0SgE01KkHs1LzX0TN.webp new file mode 100644 index 0000000000..8ea32eebf2 Binary files /dev/null and b/sites/www/content/blog/material-3-for-flutter/images/0SgE01KkHs1LzX0TN.webp differ diff --git a/sites/www/content/blog/material-3-for-flutter/images/0gjBdIy6V7lT5XGk2.gif b/sites/www/content/blog/material-3-for-flutter/images/0gjBdIy6V7lT5XGk2.gif new file mode 100644 index 0000000000..403fd392df Binary files /dev/null and b/sites/www/content/blog/material-3-for-flutter/images/0gjBdIy6V7lT5XGk2.gif differ diff --git a/sites/www/content/blog/material-3-for-flutter/images/0udIjHdleLcn1C0Ox.webp b/sites/www/content/blog/material-3-for-flutter/images/0udIjHdleLcn1C0Ox.webp new file mode 100644 index 0000000000..76b20f483e Binary files /dev/null and b/sites/www/content/blog/material-3-for-flutter/images/0udIjHdleLcn1C0Ox.webp differ diff --git a/sites/www/content/blog/material-3-for-flutter/images/1Rh5tEKBvA8ivNBGgDB7X9w.gif b/sites/www/content/blog/material-3-for-flutter/images/1Rh5tEKBvA8ivNBGgDB7X9w.gif new file mode 100644 index 0000000000..f185846bf4 Binary files /dev/null and b/sites/www/content/blog/material-3-for-flutter/images/1Rh5tEKBvA8ivNBGgDB7X9w.gif differ diff --git a/sites/www/content/blog/material-3-for-flutter/index.md b/sites/www/content/blog/material-3-for-flutter/index.md new file mode 100644 index 0000000000..4f8205389e --- /dev/null +++ b/sites/www/content/blog/material-3-for-flutter/index.md @@ -0,0 +1,118 @@ +--- +title: "Material 3 for Flutter" +description: "Sherlock Holmes and Dr Watson; peanut butter and jam; Flutter and Material! These matches are made in heaven!" +publishDate: 2023-01-18 +author: esouthren +image: images/0RuCmIqSnK0G0kHmy.webp +category: announcements +layout: blog +--- + +<DashImage figure src="images/0RuCmIqSnK0G0kHmy.webp" /> + + +## Material 3 for Flutter + +### Sherlock Holmes and Dr Watson; peanut butter and jam; Flutter and Material! These matches are made in heaven! + +[Material](https://m3.material.io/) is a design system that helps you build beautiful, accessible apps across mobile, web, and desktop. + +In Flutter, the [Material library](https://api.flutter.dev/flutter/material/Material-class.html) provides developers with all the building blocks your UI needs. Since the launch of Material 3 at Google I/O 2021, the Flutter team has been updating Flutter’s Material library to support these new changes. + +I joined the Material for Flutter team in August 2022. Since then, I’ve been helping update widgets to Material 3 specifications, while supporting Material 3 users in the open source Flutter community. + +In this article, I’ll show you the new features in Material 3, how to integrate them into your Flutter app, and update you on what’s coming next. + +<DashImage figure src="images/06JGBM5itMcIMnvYV.webp" alt="The Flutter Material 3 sample app" caption="The Flutter Material 3 sample app" /> + + +## Material 3 + +To see all the latest Material 3 updates, check out our [sample app](https://flutter.github.io/samples/web/material_3_demo/#/). It allows you to interact with all of the widgets live. + +This app demonstrates the updated components and lets you toggle between Material 2 and Material 3, light mode and dark mode, and try out different color schemes. + +## Getting started + +To add Material 3 to your app, set the [`useMaterial3`](https://api.flutter.dev/flutter/material/ThemeData/useMaterial3.html) flag to `true` in your theme’s constructor: + +```less +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + theme: ThemeData.light(useMaterial3: true), + body: MyHomePage(), + ); + } +} +``` + + +For information on migrating an existing Flutter app, check out [Migrating a Flutter app to Material 3](https://blog.codemagic.io/migrating-a-flutter-app-to-material-3/), an article by Flutter contributor Taha Tesser at CodeMagic. + +At the time of this writing, you have the *option* to use Material 3. In the future, it will become the default (and mandatory) Material version for Flutter apps. + +## New widgets + +To get most of the widget changes, set the `useMaterial3` flag. However, some widgets have changed so much in Material 3 that they’ve been replaced with new widgets. Here are some of the new widgets: + +### NavigationBar & NavigationDrawer + +The [`BottomNavigationBar`](https://api.flutter.dev/flutter/material/BottomNavigationBar-class.html) widget has been replaced with the `[NavigationBa](https://api.flutter.dev/flutter/material/NavigationBar-class.html)r`. It’s a little taller and doesn’t have a drop-shadow (that indicates elevation). + +### Segmented buttons + +Segmented buttons give your user a toggleable choice from several options *in a single widget*. By default, you can only select one item, unless you specify the `multiSelectionEnabled` parameter. + +<DashImage figure src="images/0gjBdIy6V7lT5XGk2.gif" alt="Segmented buttons — single selection and multi selection" caption="Segmented buttons — single selection and multi selection" /> + + +### Filled buttons + +We’ve increased our selection of `Button`s to include the new `[FilledButton](https://m3.material.io/components/buttons/guidelines#07a1577b-aaf5-4824-a698-03526421058b).` This button creates a colored, non-elevated button. The `FilledButton.tonal`method sets current background as a secondary color. + +<DashImage figure src="images/1Rh5tEKBvA8ivNBGgDB7X9w.gif" alt="Elevated, Filled, and Outlined buttons" caption="Elevated, Filled, and Outlined buttons" /> + + +### Badges + +The new [`Badge`](https://master-api.flutter.dev/flutter/material/Badge-class.html) widget can be used to give extra information, generally indicating a count or change of status, to a parent widget. They’re available in large and small sizes: + +<DashImage figure src="images/0SgE01KkHs1LzX0TN.webp" alt="Badge widgets attached to parent Icons" caption="Badge widgets attached to parent Icons" /> + + +## Generating a color scheme + +A brand new feature of Material 3 allows you to set the color theme of your *entire* app from a single seed color. Set the color scheme seed parameter in your theme constructor and, from that *one* entry, Flutter generates a harmonious color scheme for **every** widget in your app. This scheme works in both light *and* dark modes! + +```less +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + theme: ThemeData.light( + useMaterial3: true, + colorSchemeSeed: Color.fromRGBO(188, 0, 74, 1.0); + ), + body: MyHomePage(), + ); + } +``` + + +<DashImage figure src="images/0udIjHdleLcn1C0Ox.webp" /> + + +<DashImage figure src="images/0IM7o73HEWHQMgHUe.webp" alt="Color schemes and tint values generated from a seed color" caption="Color schemes and tint values generated from a seed color" /> + + +If you’d like to customize your color scheme further, visit the [Material generator app](https://m3.material.io/theme-builder#/custom). You can define secondary and tertiary colors to create a completely unique color palette. Best of all, it exports Dart files that you can use immediately in your Flutter app. + +## What’s next? + +Flutter’s migration to Material 3 continues. You can follow the progress at the [Bring Material 3 to Flutter](https://github.com/flutter/flutter/issues/91605#issuecomment-1264995277) GitHub issue. You can also contribute! We welcome bug reports and fixes that make Material 3 even better for Flutter developers like you. + +Our most recent update brought the [`SnackBar`](https://api.flutter.dev/flutter/material/SnackBar-class.html), [`TabBar`](https://api.flutter.dev/flutter/material/TabBar-class.html) and [`Slider`](https://api.flutter.dev/flutter/material/Slider-class.html)widgets up to Material 3 spec. So far, we’ve migrated 27/30 components, and added a range of new features to support dynamic color, text themes, Android 12 functionality, and more. + +If you’d like to get more involved with the Flutter community, head over to [flutter.dev/community](https://flutter.dev/community) and find us on your favorite social platform. See you there! \ No newline at end of file diff --git a/sites/www/content/blog/material-range-slider-in-flutter/images/0B0bu8hC9PtI5xXBL.webp b/sites/www/content/blog/material-range-slider-in-flutter/images/0B0bu8hC9PtI5xXBL.webp new file mode 100644 index 0000000000..437af21b74 Binary files /dev/null and b/sites/www/content/blog/material-range-slider-in-flutter/images/0B0bu8hC9PtI5xXBL.webp differ diff --git a/sites/www/content/blog/material-range-slider-in-flutter/images/0HLlGFubMmDajfdYU.webp b/sites/www/content/blog/material-range-slider-in-flutter/images/0HLlGFubMmDajfdYU.webp new file mode 100644 index 0000000000..c91428646e Binary files /dev/null and b/sites/www/content/blog/material-range-slider-in-flutter/images/0HLlGFubMmDajfdYU.webp differ diff --git a/sites/www/content/blog/material-range-slider-in-flutter/images/0Mu435B7sBkJHMuY4.webp b/sites/www/content/blog/material-range-slider-in-flutter/images/0Mu435B7sBkJHMuY4.webp new file mode 100644 index 0000000000..8a5813bee3 Binary files /dev/null and b/sites/www/content/blog/material-range-slider-in-flutter/images/0Mu435B7sBkJHMuY4.webp differ diff --git a/sites/www/content/blog/material-range-slider-in-flutter/images/0kChVA0QLDWygcEn0.webp b/sites/www/content/blog/material-range-slider-in-flutter/images/0kChVA0QLDWygcEn0.webp new file mode 100644 index 0000000000..c7aea7a970 Binary files /dev/null and b/sites/www/content/blog/material-range-slider-in-flutter/images/0kChVA0QLDWygcEn0.webp differ diff --git a/sites/www/content/blog/material-range-slider-in-flutter/images/0mlCfCFNzbUsT6P5l.webp b/sites/www/content/blog/material-range-slider-in-flutter/images/0mlCfCFNzbUsT6P5l.webp new file mode 100644 index 0000000000..d039a5c223 Binary files /dev/null and b/sites/www/content/blog/material-range-slider-in-flutter/images/0mlCfCFNzbUsT6P5l.webp differ diff --git a/sites/www/content/blog/material-range-slider-in-flutter/images/0tqIVrQZCHKLeLZCV.webp b/sites/www/content/blog/material-range-slider-in-flutter/images/0tqIVrQZCHKLeLZCV.webp new file mode 100644 index 0000000000..6e12c00660 Binary files /dev/null and b/sites/www/content/blog/material-range-slider-in-flutter/images/0tqIVrQZCHKLeLZCV.webp differ diff --git a/sites/www/content/blog/material-range-slider-in-flutter/images/0vcWwbYKPKh0Q-Cbc.webp b/sites/www/content/blog/material-range-slider-in-flutter/images/0vcWwbYKPKh0Q-Cbc.webp new file mode 100644 index 0000000000..9ee70fbb08 Binary files /dev/null and b/sites/www/content/blog/material-range-slider-in-flutter/images/0vcWwbYKPKh0Q-Cbc.webp differ diff --git a/sites/www/content/blog/material-range-slider-in-flutter/images/0w97BioIrNzfHqQ43.webp b/sites/www/content/blog/material-range-slider-in-flutter/images/0w97BioIrNzfHqQ43.webp new file mode 100644 index 0000000000..25fc854d80 Binary files /dev/null and b/sites/www/content/blog/material-range-slider-in-flutter/images/0w97BioIrNzfHqQ43.webp differ diff --git a/sites/www/content/blog/material-range-slider-in-flutter/images/0wtmSldtwjSvtRtuI.webp b/sites/www/content/blog/material-range-slider-in-flutter/images/0wtmSldtwjSvtRtuI.webp new file mode 100644 index 0000000000..754ad19c77 Binary files /dev/null and b/sites/www/content/blog/material-range-slider-in-flutter/images/0wtmSldtwjSvtRtuI.webp differ diff --git a/sites/www/content/blog/material-range-slider-in-flutter/index.md b/sites/www/content/blog/material-range-slider-in-flutter/index.md new file mode 100644 index 0000000000..6ae92b9aef --- /dev/null +++ b/sites/www/content/blog/material-range-slider-in-flutter/index.md @@ -0,0 +1,338 @@ +--- +title: "Material Range Slider in Flutter" +description: "What is it? Why might you use it? How do you theme it?" +publishDate: 2019-07-11 +author: clocksmith +image: images/0wtmSldtwjSvtRtuI.webp +category: tutorial +layout: blog +--- + +## Material RangeSlider in Flutter + +The range slider, a highly customizable component for selecting a range of values, has been released in Flutter 1.7. This article explains what a range slider is, why you might use it, and how you can use Material Theming to customize the behavior and appearance of the Flutter `RangeSlider`.e +> Note: To learn about updates to the `Slider` and `RangeSlider` widgets, see [What’s new with the Slider widget?](https://medium.com/flutter/whats-new-with-the-slider-widget-ce48a22611a3) + +### Why Range Slider? + +A slider component can offer a single selection or multiple selections on either a discrete or continuous track. Unlike single selection sliders that predetermine either a minimum or maximum value with the ability to adjust the selection in one direction, range sliders have two selection points that allow for a flexible adjustment of minimum and maximum value points. This flexibility makes it a useful component for instances when a user prefers to control a specific range, such as indicating price points or a length of time. + +<DashImage figure src="images/0Mu435B7sBkJHMuY4.webp" /> + + +### Structure & Implementation + +<DashImage figure src="images/0wtmSldtwjSvtRtuI.webp" /> + + +The `RangeSlider` consists of 5 parts: + +1. A track that the thumbs slide across. + +1. Tick marks on the track when the `RangeSlider` is discrete. + +1. 2 thumbs (or knobs) that indicate the min and max value of the range. + +1. Value indicators that show the labels for the thumb values when labels are defined and `showValueIndicator` matches the type of slider. + +1. Overlays that display on the thumbs when they are pressed. + +We needed the `RangeSlider` to have rich animations. This includes the interaction-driven animations for the positions of the thumb, as well as the built-in animations for the overlay and value indicators. In Flutter, we do this by making the `RangeSlider` component a `StatefulWidget`, which stores the animation controllers as state. + +The actual range slider values are stored as state in the parent widget. The values are updated by calling `setState()` within the `RangeSlider`’s `onChange()` callback. In other words, in order to have an interactive range slider, the `RangeSlider` widget itself must be created within a `StatefulWidget`. + +```dart +RangeValues _values = RangeValues(0.3, 0.7); + +RangeSlider( + values: _values, + onChanged: (RangeValues values) { + setState(() { + _values = values; + }); + }, +); +``` + +<DashImage figure src="images/0B0bu8hC9PtI5xXBL.webp" /> + + +The `RangeSlider`’s `State` object builds a `LeafRenderObjectWidget`. Everything is painted in its inner `RenderBox`, which also handles touch input. + +### Handling Touch Input + +If you are curious about how `RangeSlider` implements touch input, read on! One interesting aspect of `RangeSlider` is that it’s one of the only out-of-the-box Flutter widgets that uses a `GestureArenaTeam`. The next section covers how to customize touch input. + +If you have no interest in peeking under the hood, feel free to skip this section. + +To ensure that `RangeSlider` can handle both taps and drags while functioning properly within scroll views, tab bar views, and other widgets that handle gestures, a `GestureArenaTeam` is used. A `GestureArenaTeam` allows for a gesture within a group of gestures to be properly chosen by “winning.” + +First, the drag recognizer is added to the team, followed by the tap recognizer. There is no team captain, so the drag recognizer wins, since it was the first recognizer added to the team, as soon as any other recognizers are out of the arena. On the other hand, if the tap can win outright, such as when the slider is within a vertically scrolling list and the user taps then immediately lifts, then the tap recognizer wins. + +The drag and tap events resolve to 1 of 3 possible interactions: + +* Drag `onStart` or Tap `onTapDown` →`_startInteraction` + +* Drag `onUpdate` →`_handleDragUpdate` + +* Drag `onEnd` or `onCancel` and Tap `onEnd` or `onCancel` → `_endInteraction` + +At the start of interaction, one of the very first things that must be determined is which thumb should be selected for movement. The `RangeSlider` does this by using a themable function that takes in properties like the tap value and drag displacement, and returns a thumb choice: `Thumb.start`, `Thumb.end`, or `null` for no selection. + +The default thumb selector first attempts to find the closest thumb in `_startInteraction`. If a thumb is selected, then the thumb’s position is immediately updated to the tap value. But if the tap value is between the thumbs, but not in either touch target, there is no selection. Also, if the thumbs are close enough together, and the tap is in both touch targets, no thumb is selected. In this case, a thumb is only selected once there is a non-zero movement (drag displacement). Then the left thumb is selected for negative movement, and the right thumb is selected for positive movement. This is the only scenario where the interaction actually begins in the first `_handleDragUpdate` step. In either case, a special callback, `onChangeStart()`, emits the start values of this interaction. + +When the thumbs are further apart, touching the inner track does not select a thumb: + +<DashImage figure src="images/0mlCfCFNzbUsT6P5l.webp" /> + + +When the thumbs are closer together, the drag displacement is used to determine the thumb selection: + +<DashImage figure src="images/0vcWwbYKPKh0Q-Cbc.webp" /> + + +Implementation of the default thumb selector with the behavior described above: + +```dart + static final RangeThumbSelector _defaultRangeThumbSelector = ( + TextDirection textDirection, + RangeValues values, + double tapValue, + Size thumbSize, + Size trackSize, + double dx, // drag displacement + ) { + final double touchRadius = math.max(thumbSize.width, RangeSlider._minTouchTargetWidth) / 2; + + final bool inStartTouchTarget = (tapValue - values.start).abs() * trackSize.width < touchRadius; + + final bool inEndTouchTarget = (tapValue - values.end).abs() * trackSize.width < touchRadius; + + + if (inStartTouchTarget && inEndTouchTarget) { + bool towardsStart; + bool towardsEnd; + switch (textDirection) { + case TextDirection.ltr: + towardsStart = dx < 0; + towardsEnd = dx > 0; + break; + case TextDirection.rtl: + towardsStart = dx > 0; + towardsEnd = dx < 0; + break; + } + if (towardsStart) + return Thumb.start; + if (towardsEnd) + return Thumb.end; + } else { + if (tapValue < values.start || inStartTouchTarget) + return Thumb.start; + if (tapValue > values.end || inEndTouchTarget) + return Thumb.end; + } + return null; + }; +``` + +After a thumb is selected, all future drag updates are used to determine the new positions of the thumb. The overlay animation starts on the selected thumb, and the value indicator animations start on both thumbs. As the user drags the selected thumb, the range slider emits a new set of values with the updated position, and the values are then passed back to the range slider to update its corresponding position. + +The last step is `_endInteraction`. Once the tap or drag gesture is lifted, the overlay and value indicator animations that were started in the first step are reversed. A special callback, `onChangeEnd()`, also emits the end values. + +### Custom Touch Input Selection + +In the previous section, you saw the code for Material’s default thumb selection behavior. **But what if you wanted something different?** The following code shows how to write a thumb selector that always selects the closest thumb, regardless of what part of the track is touched. + +<DashImage figure src="images/0HLlGFubMmDajfdYU.webp" /> + + +Implementation of a custom thumb selector that always finds the closest thumb: + +```dart + static final RangeThumbSelector _customRangeThumbSelector = ( + TextDirection textDirection, + RangeValues values, + double tapValue, + Size thumbSize, + Size trackSize, + double dx, + ) { + final double start = (tapValue - values.start).abs(); + final double end = (tapValue - values.end).abs(); + return start < end ? Thumb.start : Thumb.end; + }; +``` + +Once you have this custom thumb, you can set it in the global app theme: + +```dart +theme: ThemeData( + sliderTheme: SliderThemeData( + thumbSelector: _customRangeThumbSelector, + ), + // ... +), +``` + +Or it can be set on a specific slider instance using the SliderTheme: + +```dart +SliderTheme( + data: SliderThemeData( + thumbSelector: _customRangeThumbSelector, + // ... + ), + child: RangeSlider( + // ... + ), +), +``` + +### Controlling Allowed Thumb Positions + +Above, you saw how to use the SliderThemeData to customize how the thumbs are selected. This section shows how to limit the positions that the thumbs can be dragged, or set, to. There are 2 ways to control the allowed positions of thumbs. It can be done by **value**, or it can be done by **space.** By value can be useful, for example, if you have a price selector. Let’s say the allowed prices can be within $0 and $100, but you want the range to be at least $20 apart. So the range [$30, $50] would be allowed but the range [$33, $34] would not be allowed. Simply adjust the onChanged function as follows: + +<DashImage figure src="images/0tqIVrQZCHKLeLZCV.webp" /> + + +```dart +RangeSlider( + values: _values, + min: 0, + max: 100, + onChanged: (RangeValues values) { + setState(() { + if (values.end - values.start >= 20) { + _values = values; + } else { + if (_values.start == values.start) { + _values = RangeValues(_values.start, _values.start + 20); + } else { + _values = RangeValues(_values.end - 20, _values.end); + } + } + }); + } +); + +``` + +If it is only necessary to restrict the thumbs for the sake of appearances, then the minThumbSeparation property can be used to limit the number of logical pixels that separate the 2 thumbs. The default top thumb will draw a white outline around itself for better contrast between the thumbs. Here is a side by side comparison showing the default value of 8 vs a custom value of 24 + +<DashImage figure src="images/0kChVA0QLDWygcEn0.webp" /> + + +### Painting Shapes + +In addition to handling touch input, the `RenderBox` is also responsible for painting the `RangeSlider`. It paints the `RangeSlider`’s components in this order: + +1. Track + +1. Overlays + +1. Tick Marks (if discrete) + +1. Value Indicators (if visible) + +1. Thumbs + +This can be important to know when painting custom shapes. All shape implementations are abstracted away from the `RenderBox.paint()` method through 5 separate abstract classes, which makes the painting or rendering of the `RangeSlider` fully customizable and themable since the classes exist on the `SliderThemeData` object. + +In the next section, we will show how to override the default shapes with custom shapes. + +### Using Custom Shapes + +Just like the single Slider, all of the shapes that make up a slider can be customized for the RangeSlider. See [this clip](https://www.youtube.com/watch?v=YSULAJf6R6M&t=410s) for an example of how a Material Slider was customized. + +This is done by passing custom implementations of the abstract shape classes into the SliderThemeData. This takes advantage of the RangeSliderThumbShape class to provide custom thumbs that have different appearances depending on what side they are on. + +The custom range thumb shape can be implemented as follows: + +```dart +class _CustomRangeThumbShape extends RangeSliderThumbShape { + static const double _thumbSize = 4.0; + + @override + Size getPreferredSize(bool isEnabled, bool isDiscrete) => _thumbSize; + + @override + void paint( + PaintingContext context, + Offset center, { + @required Animation<double> activationAnimation, + @required Animation<double> enableAnimation, + bool isDiscrete = false, + bool isEnabled = false, + bool isOnTop, + @required SliderThemeData sliderTheme, + TextDirection textDirection, + Thumb thumb, + }) { + final Canvas canvas = context.canvas; + + Path thumbPath; + switch (textDirection) { + case TextDirection.rtl: + switch (thumb) { + case Thumb.start: + thumbPath = _rightTriangle(_thumbSize, center); + break; + case Thumb.end: + thumbPath = _leftTriangle(_thumbSize, center); + break; + } + break; + case TextDirection.ltr: + switch (thumb) { + case Thumb.start: + thumbPath = _leftTriangle(_thumbSize, center); + break; + case Thumb.end: + thumbPath = _rightTriangle(_thumbSize, center); + break; + } + break; + } + canvas.drawPath(thumbPath, Paint()..color = sliderTheme.thumbColor); + } +} + +Path _rightTriangle(double size, Offset thumbCenter, {bool invert = false}) { + final Path thumbPath = Path(); + final double halfSize = size / 2.0; + final double sign = invert ? -1.0 : 1.0; + thumbPath.moveTo(thumbCenter.dx + halfSize * sign, thumbCenter.dy); + thumbPath.lineTo(thumbCenter.dx - halfSize * sign, thumbCenter.dy - size); + thumbPath.lineTo(thumbCenter.dx - halfSize * sign, thumbCenter.dy + size); + thumbPath.close(); + return thumbPath; +} + +Path _leftTriangle(double size, Offset thumbCenter) => _rightTriangle(size, thumbCenter, invert: true); +``` + +Then the custom range thumb shape can be set on a `SliderThemeData`: + +```dart +SliderTheme( + data: SliderThemeData( + rangeThumbShape: _CustomRangeThumbShape(), + // ... + ), + child: RangeSlider( + // ... + ), +), +``` + +<DashImage figure src="images/0w97BioIrNzfHqQ43.webp" /> + + +### Closing Remarks + +The Material range slider is a component that was requested by the community. It works out-of-the-box, and is also customizable to suit the needs of your app. The behavior and visual appearance can be changed in the theme at the global level, or on an instance by instance basis. + +The complete code for all code included in this article, along with more examples, can be found in the [Material gallery on github](https://github.com/flutter/flutter/tree/master/examples/flutter_gallery/lib/demo/material) and in the [Material library on github](https://github.com/flutter/flutter/tree/master/packages/flutter/lib/src/material). + +Special thanks to Shams Zakhour, Liam Spradlin, Barbara Eldredge, Cortney Cassidy, and Will Larche. \ No newline at end of file diff --git a/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/18hlXVoJUQVz3piOiCsg-gQ.gif b/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/18hlXVoJUQVz3piOiCsg-gQ.gif new file mode 100644 index 0000000000..de5f260032 Binary files /dev/null and b/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/18hlXVoJUQVz3piOiCsg-gQ.gif differ diff --git a/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/1CXyt0NsoasPW5__Ev4AGNg.webp b/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/1CXyt0NsoasPW5__Ev4AGNg.webp new file mode 100644 index 0000000000..226c23883f Binary files /dev/null and b/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/1CXyt0NsoasPW5__Ev4AGNg.webp differ diff --git a/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/1CawKl49B-kWc1XoPGHVTaw.gif b/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/1CawKl49B-kWc1XoPGHVTaw.gif new file mode 100644 index 0000000000..cfce143f27 Binary files /dev/null and b/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/1CawKl49B-kWc1XoPGHVTaw.gif differ diff --git a/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/1fK1Ra4g1hYe0nGwRFj4nCQ.gif b/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/1fK1Ra4g1hYe0nGwRFj4nCQ.gif new file mode 100644 index 0000000000..baee242e2e Binary files /dev/null and b/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/1fK1Ra4g1hYe0nGwRFj4nCQ.gif differ diff --git a/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/1iiDlKE9ikBNvUMHvr6YWSw.gif b/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/1iiDlKE9ikBNvUMHvr6YWSw.gif new file mode 100644 index 0000000000..56196e76a9 Binary files /dev/null and b/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/1iiDlKE9ikBNvUMHvr6YWSw.gif differ diff --git a/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/1tOfRiFIWVkDO3IXLRFcnEw.gif b/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/1tOfRiFIWVkDO3IXLRFcnEw.gif new file mode 100644 index 0000000000..a8426cd724 Binary files /dev/null and b/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/1tOfRiFIWVkDO3IXLRFcnEw.gif differ diff --git a/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/1xmF3JHqzQi-d9W79JcscZw.gif b/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/1xmF3JHqzQi-d9W79JcscZw.gif new file mode 100644 index 0000000000..fb66adf187 Binary files /dev/null and b/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/images/1xmF3JHqzQi-d9W79JcscZw.gif differ diff --git a/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/index.md b/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/index.md new file mode 100644 index 0000000000..6efc06b733 --- /dev/null +++ b/sites/www/content/blog/meet-the-flutter-extension-for-gemini-cli/index.md @@ -0,0 +1,117 @@ +--- +title: "Meet the Flutter Extension for Gemini CLI" +description: "Build high quality, feature rich apps with the new Flutter Extension for Gemini CLI" +publishDate: 2025-10-08 +author: johnpryan +image: images/1CXyt0NsoasPW5__Ev4AGNg.webp +category: spotlight +layout: blog +--- + +<DashImage figure src="images/1CXyt0NsoasPW5__Ev4AGNg.webp" /> + + +## Meet the Flutter Extension for Gemini CLI + +### Build high quality, feature rich apps with the new Flutter Extension for Gemini CLI + +Today we’re excited to share the first experimental version of the **Flutter Extension for Gemini CLI**, which combines the [Dart and Flutter MCP Server](https://blog.flutter.dev/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server-2edcc8107b49) with additional context and commands, making it easier and more productive to build Flutter apps with [Gemini CLI](https://geminicli.com/). + +AI agents are changing the way we build Flutter apps by assisting with tasks like feature prototyping, code reviews, as well as writing and running tests. In order to use an AI agent effectively, you need to provide it with context and access to tools to help it become a productive Flutter coding assistant. This is where the Flutter Extension for Gemini CLI comes in. [Gemini CLI extensions](https://blog.google/technology/developers/gemini-cli-extensions) allow you to build integrations with Gemini CLI and your tools, and the Flutter extension builds off of these capabilities. + +The Flutter Extension for Gemini CLI provides commands to accelerate app development, follows explicit rules to write high-quality code following Dart and Flutter best-practices, and runs tools from the Dart and Flutter MCP server to directly access Dart and Flutter’s developer tools. You spend less time on setup and more time building high quality Flutter apps. + +## See it in action + +To get started, install the extension with the following command in your terminal: + +```ruby +$ gemini extensions install https://github.com/gemini-cli-extensions/flutter +``` + + +The Flutter Extension for Gemini CLI comes with a set of commands to help you with critical stages of development. Here are a few of the most common commands and how to use them: + +* `/create-app`: Creates a design doc and implementation plan for Gemini to follow. + +* `/modify`: Creates a plan to make large changes to the app. + +* `/commit`: Analyzes and tests the code, fixes any issues that were found, and then commits the change to Git. + +To learn more about how to set up and use the Gemini CLI extension, follow the instructions in the [GitHub repository](https://github.com/gemini-cli-extensions/flutter). + +### Create an app + +You can create a new application using the `/create-app` command. This command bootstraps a brand-new, production-ready Flutter app. It goes beyond `flutter create` by asking for your app’s purpose, setting up recommended linter rules, and generating detailed `DESIGN.md` and `IMPLEMENTATION.md` files for your review before any code is written. + +```bash +/create-app +``` + + +<DashImage figure src="images/1xmF3JHqzQi-d9W79JcscZw.gif" alt="Create app app with the /create-app command" caption="Create app app with the /create-app command" /> + + +The `DESIGN.md` file is a design document for the app; it specifies the problems that the app solves and provides technical details about how it will work. You can edit this file before you continue with the implementation steps, allowing you to guide Gemini to build the exact app that you’re looking for. + +Once the design is ready, `/create-app` generates an `IMPLEMENTATION.md` file, a step-by-step implementation plan, so that it can iteratively work on feature implementation. It keeps a record of its progress, so you can pause and restart. By default, `/create-app` splits the plan up into 3–5 phases, where each phase is a logical stopping point. After each phase, Gemini will analyze and format the code, run tests, and commit the changes. It will also update this file after it completes a phase in the Journal section. + +### Implement features from the plan + +After you’ve set up your project, you’re ready to implement the features in your implementation plan using the generated `IMPLEMENTATION.md` file. Each feature will be implemented separately, as outlined in this file. Once it finishes implementing a feature, the Flutter Extension will mark it as complete. + +Before moving to the next phase, the extension will ask for your approval. You can enter the prompt “looks good” to start generating code: + +<DashImage figure src="images/1fK1Ra4g1hYe0nGwRFj4nCQ.gif" alt="The guided implementation plan keeps Gemini CLI on track" caption="The guided implementation plan keeps Gemini CLI on track" /> + + +### Modify + +To make changes to existing code, the `/modify` command initiates a guided development session. It asks for your goals, offers to create a new branch, and generates a `MODIFICATION_PLAN.md` design doc outlining the proposed modifications and a phased implementation plan. + +```bash +/modify +``` + + +<DashImage figure src="images/18hlXVoJUQVz3piOiCsg-gQ.gif" alt="Use the /modify command to create a detailed modification plan for Gemini to follow" caption="Use the /modify command to create a detailed modification plan for Gemini to follow" /> + + +### Clean up and commit + +The final step is to commit the changes using `/commit`. This command prepares your changes before committing them with Git. It automatically runs `dart fix` and `dart format`, runs the analyzer and tests, and then generates a descriptive commit message based on the changes for you to approve. + +<DashImage figure src="images/1CawKl49B-kWc1XoPGHVTaw.gif" alt="Use the /commit command to clean up and prepare the code for commit" caption="Use the /commit command to clean up and prepare the code for commit" /> + + +## Fully loaded with best-practices + +Every interactive chat session includes rules containing best-practices for Flutter and Dart development. These rules ensure that Gemini writes high-quality Dart and Flutter code, interacts with MCP server tools correctly, and follows best practices such as creating unit tests, writing documentation, ensuring accessibility, and more. + +<DashImage figure src="images/1tOfRiFIWVkDO3IXLRFcnEw.gif" alt="The extension includes rules and best-practices" caption="The extension includes rules and best-practices" /> + + +## Access to development tools with the Flutter and Dart MCP server + +The Dart and Flutter MCP server is automatically configured when you install the Flutter Extension for Gemini CLI. This allows Gemini CLI and other AI agents to perform common development tasks: + +* **Analyze and fix errors** in your project’s code. + +* **Introspect and interact with your running application** (such as trigger a hot reload, get the selected widget, fetch runtime errors). + +* **Search pub.dev** for the best package for your use case. + +* **Manage package dependencies** in your `pubspec.yaml` file. + +* **Run tests** and analyze the results. + +* …and more! + +<DashImage figure src="images/1iiDlKE9ikBNvUMHvr6YWSw.gif" alt="The MCP server provides Gemini CLI access to Dart and Flutter DevTools" caption="The MCP server provides Gemini CLI access to Dart and Flutter DevTools" /> + + +## Try it out + +We hope you try out the Flutter extension for Gemini CLI and share your feedback. + +Check out the [Flutter extension on GitHub](https://github.com/gemini-cli-extensions/flutter) to learn more. If you have suggestions or run into problems, please [file an issue](https://github.com/gemini-cli-extensions/flutter/issues). We can’t wait to see what you build! \ No newline at end of file diff --git a/sites/www/content/blog/modern-flutter-plugin-development/images/1I4K18qd0H98EZixwMmWGmg.webp b/sites/www/content/blog/modern-flutter-plugin-development/images/1I4K18qd0H98EZixwMmWGmg.webp new file mode 100644 index 0000000000..3f79ea7bd0 Binary files /dev/null and b/sites/www/content/blog/modern-flutter-plugin-development/images/1I4K18qd0H98EZixwMmWGmg.webp differ diff --git a/sites/www/content/blog/modern-flutter-plugin-development/images/1V4h-nurkyQZvRIjJFifWUQ.webp b/sites/www/content/blog/modern-flutter-plugin-development/images/1V4h-nurkyQZvRIjJFifWUQ.webp new file mode 100644 index 0000000000..e9c6d62c32 Binary files /dev/null and b/sites/www/content/blog/modern-flutter-plugin-development/images/1V4h-nurkyQZvRIjJFifWUQ.webp differ diff --git a/sites/www/content/blog/modern-flutter-plugin-development/images/1yM2BlF_ckdUzoDnSdAjPoA.webp b/sites/www/content/blog/modern-flutter-plugin-development/images/1yM2BlF_ckdUzoDnSdAjPoA.webp new file mode 100644 index 0000000000..7b49558687 Binary files /dev/null and b/sites/www/content/blog/modern-flutter-plugin-development/images/1yM2BlF_ckdUzoDnSdAjPoA.webp differ diff --git a/sites/www/content/blog/modern-flutter-plugin-development/index.md b/sites/www/content/blog/modern-flutter-plugin-development/index.md new file mode 100644 index 0000000000..e0be995624 --- /dev/null +++ b/sites/www/content/blog/modern-flutter-plugin-development/index.md @@ -0,0 +1,433 @@ +--- +title: "Modern Flutter Plugin Development" +description: "by Amir Hardon, Chris Sells, Collin Jackson, Harry Terkelsen and Matt Carroll" +publishDate: 2020-04-30 +author: csells +image: images/1yM2BlF_ckdUzoDnSdAjPoA.webp +category: announcements +layout: blog +--- + +2019 was a big year for advancements in technology for Flutter plugin authors. We introduced the Android Plugin API 2.0, which provides a more robust and full-featured way to implement Android support in your plugins. We updated the `pubspec.yaml` format to make it possible to clearly specify Android and iOS support but also web, macOS, Windows, and Linux. Furthermore, as we push Flutter to support multiple platforms, we enabled federation, making it possible for multiple teams with different expertise to bring their code together into a seamless experience for the Flutter developer using the plugin. And finally, we made great strides in testing plugins, with more to come. + +## Android Plugin API 2.0 + +In December of 2019, Flutter published a new version of its Android embedding. This is the Android code that is responsible for integrating Flutter within an Android app. It includes classes like `FlutterActivity`, `FlutterFragment`, `FlutterView`, and `FlutterEngine`. The v2 Android embedding includes support for standard Android lifecycle events and the separation of Flutter execution from the Android UI, which were missing in the v1 Android embedding. During the development of the v2 Android embedding it became clear that the existing API for Flutter plugins was insufficient to handle the new capabilities of the v2 Android embedding. A new Android plugin API was needed. We’ll discuss that API and how to use it. + +First, it is important to understand the `FlutterEngine` class within the v2 Android embedding. A `FlutterEngine` object represents a single Flutter execution context. This means that a `FlutterEngine` controls a Dart isolate (your Dart code that begins with an entrypoint like `main`). It also means that a `FlutterEngine` sets up a number of standard platform channels that all Flutter apps require; it includes support for platform views, it knows how to paint a texture with a Flutter UI, and it handles all other fundamental requirements to execute a single Flutter/Dart app. Additionally, an Android app may contain multiple `FlutterEngines` at the same time. + +The fundamental concept of “adding a plugin” to a Flutter app means applying that plugin to a single `FlutterEngine`. For example, if a Flutter app requires access to a camera, that capability is achieved by registering a camera plugin with a specific `FlutterEngine` instance. This registration is done for you automatically with a `GeneratedPluginRegistrant`, but it is important to understand that every `FlutterEngine` maintains its own set of Flutter plugins. + +<DashImage figure src="images/1V4h-nurkyQZvRIjJFifWUQ.webp" /> + + +In the old v1 Android embedding, all plugins were initialized and configured at the very beginning of the Android app and there was only ever one Flutter experience. In the v2 embedding, we make no assumption about when a plugin is initialized, and a plugin must be initialized once per `FlutterEngine`. As a result, all Flutter plugins for Android must now support instantiation instead of static initialization, and they must support being attached to, and detached from a `FlutterEngine`. The following code samples demonstrate the difference between the old v1 plugin initialization implementation and the new v2 plugin initialization process. + +**Old plugin initialization** + +``` +class MyOldPlugin { + public static void registerWith(PluginRegistrar registrar) { + // Obtain any references that the plugin requires from the + // registrar. + // + // This plugin is now considered "initialized" and "attached" + // to a Flutter experience. + } +} +``` + + +**New plugin initialization** + +``` +*class* MyNewPlugin implements FlutterPlugin { + public MyNewPlugin() { + // All Android plugin classes must support a no-args + // constructor. A no-arg constructor is provided by + // default without declaring one, but we include it here for + // clarity. + // + // At this point your plugin is instantiated, but it + // isn't attached to any Flutter experience. You should not + // attempt to do any work here that is related to obtaining + // resources or manipulating Flutter. + } + + @override + public void onAttachedToFlutterEngine(FlutterPluginBinding binding) { + // Your plugin is now attached to a Flutter experience + // represented by the given FlutterEngine. + // + // You can obtain the associated FlutterEngine with + // binding.getFlutterEngine() + // + // You can obtain a BinaryMessenger with + // binding.getBinaryMessenger() + // + // You can obtain the Application context with + // binding.getApplicationContext() + // + // You cannot access an Activity here because this + // FlutterEngine is not necessarily displayed within an + // Activity. See the ActivityAware interface for more info. + } + + @override + public void onDetachedFromFlutterEngine(FlutterPluginBinding binding) { + // Your plugin is no longer attached to a Flutter experience. + // You need to clean up any resources and references that you + // established in onAttachedToFlutterEngine(). + } +} +``` + + +As shown in the new plugin API, your plugin must wait until `onAttachedToFlutterEngine()` completes before taking any meaningful actions, and it must respect `onDetachedFromFlutterEngine()` by releasing all resources. It’s possible that your plugin will be attached and detached multiple times. + +Additionally, your plugin must not depend upon an `Activity` reference within `onAttachedToFlutterEngine()`. Just because your plugin is attached to a Flutter experience doesn’t mean that the Flutter experience is being displayed in an Activity. **This is one of the most significant differences between the old and new plugin APIs.** In the old v1 plugin API, plugin authors could depend upon an `Activity` being available immediately and permanently. This is no longer true. + +Plugins that require access to an `Activity` must implement a 2nd interface called `ActivityAware`. The `ActivityAware` interface adds callbacks to your plugin class that tell your plugin when it’s sitting in an `Activity`, when that `Activity` goes through config changes, and when your plugin is no longer sitting in an `Activity`. Your plugin must respect these callbacks. The following example shows the outline of an `ActivityAware` plugin: + +``` +class MyNewPlugin implements FlutterPlugin, ActivityAware { + @override + public void onAttachedToFlutterEngine(FlutterPluginBinding binding) { + // ... + } + + @override + public void onDetachedFromFlutterEngine(FlutterPluginBinding binding) { + // ... + } + + @override + public void onAttachedToActivity(ActivityPluginBinding binding) { + // Your plugin is now associated with an Android Activity. + // + // If this method is invoked, it is always invoked after + // onAttachedToFlutterEngine(). + // + // You can obtain an Activity reference with + // binding.getActivity() + // + // You can listen for Lifecycle changes with + // binding.getLifecycle() + // + // You can listen for Activity results, new Intents, user + // leave hints, and state saving callbacks by using the + // appropriate methods on the binding. + } + + @override + public void onDetachedFromActivityForConfigChanges() { + // The Activity your plugin was associated with has been + // destroyed due to config changes. It will be right back + // but your plugin must clean up any references to that + // Activity and associated resources. + } + + @override + public void onReattachedToActivityForConfigChanges( + ActivityPluginBinding binding + ) { + // Your plugin is now associated with a new Activity instance + // after config changes took place. You may now re-establish + // a reference to the Activity and associated resources. + } + + @override + public void onDetachedFromActivity() { + // Your plugin is no longer associated with an Activity. + // You must clean up all resources and references. Your + // plugin may, or may not ever be associated with an Activity + // again. + } +} +``` + + +The new plugin API explicitly recognizes that a plugin may or may not have an `Activity` associated with it, and that any such `Activity` might be destroyed and recreated at any time due to configuration changes. These concerns should be familiar to all Android developers. + +The key to writing a plugin for Flutter’s v2 Android embedding is to respect each plugin lifecycle callback that your plugin implements. As long as you wait until the right time to establish references, and you release those references at the appropriate time, your plugin will work as intended. + +<DashImage figure src="images/1I4K18qd0H98EZixwMmWGmg.webp" /> + + +Some plugins, like a camera plugin, only make sense when an `Activity` is available. So what are these plugins to do? In the case of UI-only plugins, those plugins can wait until `onAttachedToActivity()` executes before doing any work. Then, in `onDetachedFromActivity()`, those plugins can clean up all references and essentially deactivate themselves. There is no requirement that a plugin do anything special within `onAttachedToFlutterEngine()`. It’s OK for a plugin to only do work when attached to an `Activity`. + +For more details about how to migrate your Android plugin from the v1 API to the v2 API, see [Supporting the new Android plugins APIs](https://flutter.dev/docs/development/packages-and-plugins/plugin-api-migration) on flutter.dev. + +## New `pubspec` format + +Traditionally a Flutter plugin was a single package that enabled Flutter applications running on Android and iOS to access platform-specific functionality; technically a plugin was composed of Dart code backed by Android-specific and iOS-specific code. Even though the assumption that any Flutter plugin supports Android and iOS wasn’t accurate (for example, the `android_intent` plugin only supports Android), it was chosen to initially design the plugin ecosystem under that assumption. The assumption was mostly correct, which meant the overall cost for the few instances where it was wrong was low, and the simplified assumption enabled rapid progress and focus. + +With Flutter growing to support many more platforms we have decided to lose this simplifying assumption as: + +1. We expect many plugins to only support a subset of Flutter’s supported platforms (even more so with federated plugins, described below). + +1. We want to unlock tooling features that require knowledge of a plugin’s supported platforms (such as a smarter pub.dev search and platform-informed tool operations). + +The core missing piece was a clear indication for which platforms a plugin supports, and so we’ve redesigned the Flutter plugin’s `pubspec` schema around multi-platform support. + +Under the previous `pubspec` schema the `flutter.plugin` key contained the different plugin configuration bits, instead we’ve introduced a new key per platform under the `flutter.plugin.platforms` key with the platform-specific plugin configuration. For example, here’s what the `pubspec` for a plugin that supports Android, iOS, macOS, and web looks like: + +``` +flutter: + plugin: + platforms: + android: + package: com.example.hello + pluginClass: HelloPlugin + ios: + pluginClass: HelloPlugin + macos: + pluginClass: HelloPlugin + web: + pluginClass: HelloPlugin + fileName: hello_web.dart + +environment: + sdk: ">=2.1.0 <3.0.0" + # Flutter versions prior to 1.10 did not support + # the flutter.plugin.platforms map. + flutter: ">=1.10.0" +``` + + +A plugin that supports a subset of these platforms can omit platform keys from the platforms map, for example: + +``` +flutter: + plugin: + platforms: + android: + package: com.example.hello + pluginClass: HelloPlugin + ios: + pluginClass: HelloPlugin + +environment: + sdk: ">=2.1.0 <3.0.0" + # Flutter versions prior to 1.10 did not support + # the flutter.plugin.platforms map. + flutter: ">=1.10.0" +``` + + +Note that requiring a Flutter SDK greater than 1.10.0 is required when using the new schema as this is the first version where the Flutter tool supported this schema. + +## Migrating an existing plugin to use the new schema + +This section uses the battery plugin as an example and walks through the migration of a sample plugin from the previous schema to the new one. + +The most important thing to do when migrating is to only declare support for platforms that are supported by the plugin (this was not possible previously which meant that plugins which only supported Android had to include a no-op iOS implementation and vice versa). + +These are the relevant parts of a sample plugin’s `pubspec.yaml` file prior to the migration: + +``` +name: sample +version: 0.3.1+5 + +flutter: + plugin: + androidPackage: io.flutter.plugins.sample + iosPrefix: FLT + pluginClass: SamplePlugin + +environment: + flutter: ">=1.6.7 <2.0.0" +``` + + +Assuming that the plugin supports Android and iOS, upgrading to the new schema involves: + +* Increasing the minimally required Flutter version to 1.10.0 (which is the first version that supports the new schema). + +* A minor version bump + +* Replacing the current fields in `flutter.plugin` with the new platforms field. + +* If the `iosPrefix` field was previously used, renaming the main iOS plugin file (more details below). + +The updated `pubspec` for this plugin looks like: + +``` +name: sample +version: 0.3.2 + +flutter: + plugin: + platforms: + android: + package: io.flutter.plugins.sample + pluginClass: SamplePlugin + ios: + pluginClass: FLTSamplePlugin + +environment: + flutter: ">=1.10.0 <2.0.0" +``` + + +Note that since the plugin supports Android and iOS these are the only keys under the flutter.plugin.platforms. The value of `flutter.plugin.androidPackage` field in the old schema becomes the value of `flutter.plugin.platforms.android.package`. The new schema has no equivalent for the `iosPrefix` field, as the `pluginClass` for iOS has a dedicated key we can use the prefix in the `flutter.plugin.platforms.ios.pluginClass` field which is set to `FLTSamplePlugin`. + +## Plugins that were previously using the iosPrefix field + +The previous schema implied an inconsistency between the iOS plugin’s main interface name and its file name, e.g. for this sample plugin as defined with the previous schema, there would be a `SamplePlugin.h` file that declares an `FLTSamplePlugin` interface. This kind of inconsistency is no longer supported, which means the `SamplePlugin.h` file must be renamed to `FLTSamplePlugin.h` when upgrading to the new schema. Plugins that did not use the `iosPrefix` key don’t require renaming any files. + +For more information about development plugins that support any number of platforms, see [Developing plugin packages](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin) on flutter.dev. + +## Federation + +Not only does the new `pubspec` schema allow you to specify exactly which platforms your plugin supports, it also gives you the flexibility to spread implementations across multiple packages. In the past, the plugin’s Dart code, the Android Java (or Kotlin) code, and the iOS Objective-C (or Swift) code all needed to be in the same Dart package. Now, if we want to add support for another platform (Web, Mac OS, Windows, etc.), it doesn’t need to be in the same package. Plugins that are distributed across multiple packages are called *federated plugins*. + +Federated plugins have several advantages over single-package plugins, including: + +* A plugin author does not need to have domain expertise for every supported Flutter platform (Android, iOS, Web, Mac OS, etc.). + +* You can add support for a new platform without the original plugin author needing to review and pull in your code. + +* Each package can be maintained and tested separately. + +So, how exactly do you create a *federated plugin*? Let’s start with some terminology: + +* **app-facing package:** This is the package you would import in your app in order to use the plugin. For example, `package:url_launcher` is an app-facing package. The app-facing package declares the app-facing API and works with various *platform packages* to perform platform-specific functionality. + +* **platform package**: This is a package that implements the platform-specific functionality needed by the *app-facing package*. An example is `package:url_launcher_web`: this package is used by `package:url_launcher` to launch URLs when running the Flutter app on the web platform. Platform packages should not be imported in the app, they are only for use by the *app-facing package* to call into the required platform-specific code. + +* **platform interface package**: This is the glue that holds the *app-facing package* and the *platform packages* together. Whereas the *app-facing package* declares an API that can be called from a Flutter app, the *platform interface package* declares an interface that each *platform package* must implement in order to support the *app-facing package*. Having a single package that defines this interface ensures that all *platform packages* implement the same functionality in a uniform way. + +<DashImage figure src="images/1yM2BlF_ckdUzoDnSdAjPoA.webp" /> + + +The figure above shows the dependency graph between the app, the app-facing package, the platform packages, and the platform interface package. The app only ever imports the app-facing package (in this case `package:url_launcher`). + +How does the *platform interface* glue together the app-facing package and the correct platform package? In the past, there were no “platform packages”, only a subfolder for the Android code and another subfolder for the iOS code. The app-facing package communicated to the platform code via a `MethodChannel`. You can think of the `MethodChannel` as the de-facto “platform interface” because the app-facing package calls into the `MethodChannel`, and the corresponding platform code must be listening on a `MethodChannel` for the correct methods with the correct arguments. There is no way to statically confirm that the Android code or iOS code is listening for the correct `MethodChannel` calls. + +## Old way of launching a URL + +``` +Future<void> launch(String url) { + channel.invokeMethod('launch', { + 'url': url, + }); +} +``` + + +In the federated plugin structure, the *platform interface package* replaces the `MethodChannel`. The platform-specific functionality that the app-facing package needs from the platform packages is encapsulated in a platform interface. In our example, the app-facing package is `package:url_launcher`, and the only platform-specific functionality it needs is the ability to launch a URL on the given platform. A (very) simple platform interface would look like: + +``` +abstract class UrlLauncherPlatform { + /// Launches the given [url]. + Future<void> launch(String url); + + /// The live "instance" of this platform interface. + /// + /// This is set when the platform package is registered, + /// usually when the platform is initializing. + /// + /// For example, the web platform package + /// (package:url_launcher_web) would extend this class with an + /// implementation that launched the URL in a new tab, + /// and at initialization time would set itself to be the live + /// instance with: + /// + /// UrlLauncherPlatform.instance = WebUrlLauncher(); + static UrlLauncherPlatform instance; +} +``` + + +Now, instead of calling on the `MethodChannel`, the app-facing package would call into the platform interface. + +## New way of launching a URL + +``` +Future<void> launch(String url) { + return UrlLauncherPlatform.instance.launch(url); +} +``` + + +So, the *app-facing package* calls into the *platform interface*. How does the platform interface glue into the *platform packages*? The platform packages implement the platform interface, and register themselves as the default instance of the platform interface when the platform is initializing. + +For example, if we wanted to write `package:url_launcher_web`, we would only need to write a class that extends `UrlLauncherPlatform` and launches a URL for the web platform. The code would look something like this: + +``` +class UrlLauncherWeb extends UrlLauncherPlatform { + /// The web platform automatically calls this when the app + /// initializes. + static void registerWith(Registrar registrar) { + var webLauncher = UrlLauncherWeb(); + UrlLauncherPlatform.instance = webLauncher; + } + + @override + Future<void> launch(String url) => window.open(url, ''); +} +``` + + +The nice thing about migrating to a federated plugin structure, is that once you are set up with the app-facing package and platform interface package, adding support for a new platform is simple (and you don’t even have to do it yourself!) All that is required is to create a new platform package which extends the platform interface declared in the platform interface package. + +For more details about federated plugins, see [Federate plugins](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#federated-plugins) on flutter.dev. + +## Testing plugins + +As you write new cross-platform plugins or add platforms to existing ones, you can save yourself time and future headaches by writing tests. Automated tests protect your plugin from functional regressions so that you can rapidly develop new features and merge contributions. + +A well-tested plugin typically includes several styles of test spread across multiple packages. It’s possible to slow yourself down writing tests that are flaky or unlikely to never fail, so focus on the test writing that gives you confidence that the key use cases are still functional. + +## AutomatedWidgetsFlutterBinding tests + +Tests that use `AutomatedWidgetsFlutterBinding` run on the development machine, rather than a device or browser. Because of this, they run faster and some functionality needs to be provided by mocks. + +**In the app-facing package** (such as `myplugin`), the package’s unit tests ensure that calls to the app-facing API result in expected interactions with the platform interface package. These tests typically import `package:mockito` to provide a fake platform interface and verify that it receives the correct calls. Here is an [example test](https://github.com/flutter/plugins/blob/master/packages/url_launcher/url_launcher/test/url_launcher_test.dart#L25) from package:url_launcher: + +``` +test('returns true', () async { + when(mock.canLaunch('foo')).thenAnswer((_) => + Future<bool>.value(true)); + final bool result = await canLaunch('foo'); + expect(result, isTrue); +}); +``` + + +**In the platform interface package** (such as `myplugin_platform_interface`), a platform interface is an abstract class and cannot be instantiated directly. However, the platform interface package typically contains the method channel implementation of the platform interface as well, so that’s what you should test. Tests for this package should focus on the method channel invocations that result from calls to the platform interface and the method channel. These tests typically use [`setMockMethodCallHandler`](https://api.flutter.dev/flutter/services/MethodChannel/setMockMethodCallHandler.html) with the [`isMethodCall`](https://api.flutter.dev/flutter/flutter_test/isMethodCall.html) matcher to verify behavior. + +``` +test('canLaunch', () async { + await launcher.canLaunch('[http://example.com/'](http://example.com/')); + expect( + log, + <Matcher>[ + isMethodCall('canLaunch', arguments: <String, Object>{ + 'url': '[http://example.com/'](http://example.com/'), + }) + ], + ); +}); +``` + + +**In the platform tests** (such `myplugin_web`), you can take advantage of platform-specific functionality. In the current Flutter SDK, flutter test provides an experimental `—-platform` flag that allows you to choose to run tests in a Chrome-like environment where dart:html is available. + +This test mode is useful for writing tests in the platform implementation package (for example, `myplugin_web`). + +``` +test('cannot launch "tel" URLs', () { + expect(canLaunch('tel:5551234567'), completion(isFalse)); +}); +``` + + +In addition, you can run your GUI tests in Chrome using [experimental support for ‘flutter drive’ testing on the web](https://github.com/flutter/flutter/wiki/Running-Flutter-Driver-tests-with-Web). + +For more information about plugin testing, see [Testing your plugin](https://flutter.dev/docs/development/packages-and-plugins/plugin-api-migration#testing-your-plugin) on flutter.dev. + +## Summary + +As you can see, there’s a lot of new features for the Flutter plugin developer, allowing you to build more full-featured and more robust plugins across more platforms. If you’re interested in some web-specific details, I recommend Harry Terkelsen’s two-part series on How to Write a Flutter Web Plugin ([part 1](https://medium.com/flutter/how-to-write-a-flutter-web-plugin-5e26c689ea1) and [part 2](https://medium.com/flutter/how-to-write-a-flutter-web-plugin-part-2-afdddb69ece6)). For more information on writing plugins in general, the [Developing plugin packages](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin) documentation on flutter.dev is also a great resource. \ No newline at end of file diff --git a/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/00i_W2vNozOa87N_B.webp b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/00i_W2vNozOa87N_B.webp new file mode 100644 index 0000000000..443a27b27f Binary files /dev/null and b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/00i_W2vNozOa87N_B.webp differ diff --git a/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0F4TQYa2f-9bAaW3X.webp b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0F4TQYa2f-9bAaW3X.webp new file mode 100644 index 0000000000..13b3de6e42 Binary files /dev/null and b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0F4TQYa2f-9bAaW3X.webp differ diff --git a/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0J0ZaW4hZbSlEXjK9.webp b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0J0ZaW4hZbSlEXjK9.webp new file mode 100644 index 0000000000..8bbd34d2f9 Binary files /dev/null and b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0J0ZaW4hZbSlEXjK9.webp differ diff --git a/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0JZ_gkqDtOFpGnTtJ.jpg b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0JZ_gkqDtOFpGnTtJ.jpg new file mode 100644 index 0000000000..12f8b22320 Binary files /dev/null and b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0JZ_gkqDtOFpGnTtJ.jpg differ diff --git a/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0KnlTgdXeNMDAMpna.webp b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0KnlTgdXeNMDAMpna.webp new file mode 100644 index 0000000000..7685d006f3 Binary files /dev/null and b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0KnlTgdXeNMDAMpna.webp differ diff --git a/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0fL_0E_cbbMff5IIr.webp b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0fL_0E_cbbMff5IIr.webp new file mode 100644 index 0000000000..8ff11eadff Binary files /dev/null and b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0fL_0E_cbbMff5IIr.webp differ diff --git a/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0mYMsKK4g65KGh71f.webp b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0mYMsKK4g65KGh71f.webp new file mode 100644 index 0000000000..1df1871084 Binary files /dev/null and b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0mYMsKK4g65KGh71f.webp differ diff --git a/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0qUlFDIP4H0C4_urO.webp b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0qUlFDIP4H0C4_urO.webp new file mode 100644 index 0000000000..1d5ef63828 Binary files /dev/null and b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/0qUlFDIP4H0C4_urO.webp differ diff --git a/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/1eL-dHo08RwyLYOl17DNTog.webp b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/1eL-dHo08RwyLYOl17DNTog.webp new file mode 100644 index 0000000000..3a8d8e0d83 Binary files /dev/null and b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/images/1eL-dHo08RwyLYOl17DNTog.webp differ diff --git a/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/index.md b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/index.md new file mode 100644 index 0000000000..e0304aaa98 --- /dev/null +++ b/sites/www/content/blog/must-try-use-firebase-to-host-your-flutter-app-on-the-web/index.md @@ -0,0 +1,165 @@ +--- +title: "Must try: Use Firebase to host your Flutter app on the web" +description: "So you have a Flutter app, and you want to show it to the world? Look no further! This tutorial will show you how to host your Flutter app…" +publishDate: 2019-11-11 +author: clocksmith +image: images/1eL-dHo08RwyLYOl17DNTog.webp +category: spotlight +layout: blog +--- + +## Use Firebase to host your Flutter app on the web + +<DashImage figure src="images/1eL-dHo08RwyLYOl17DNTog.webp" /> + + +So you have a Flutter app, and you want to show it to the world? Look no further! This tutorial shows how to host your Flutter app on the web through a Firebase-hosted URL in just 4 easy steps (2 steps if you already have firebase-tools installed and an existing Firebase project). It is assumed that you have Flutter installed and are able to run your Flutter app locally. If you are new to Flutter, first check out the [getting started guide](https://flutter.dev/docs/get-started/install). + +## Prerequisite + +If your project was created before flutter web was enabled, your project needs a copy of the web folder with an `index.html` file. If there is not a web folder in your root directory, run the following command to make one: + +``` +$ flutter create . +``` + + +If you create a project after enabling the web, it automatically creates the `web/index.html` file for you. Good, now on to the Firebase Hosting setup. + +☝️Note: Step 1 and 2 can be skipped if you already have a Firebase project and firebase-tools installed. + +## Step 1: Create the Firebase project + +🏃‍♀ Skip this step if you already have a Firebase project to use. + +☝️Note: A new project can also be created on the command line from within step 2, but this tutorial creates it in the Firebase console. + +In a browser, go to the [Firebase console](https://console.firebase.google.com/). + +<DashImage figure src="images/0J0ZaW4hZbSlEXjK9.webp" /> + + +Click **Add project** and give your project a name: + +<DashImage figure src="images/00i_W2vNozOa87N_B.webp" /> + + +☝️Note: The actual project name might generate some extra characters at the end for uniqueness. + +Click **Continue.** For simplicity, don’t enable Google Analytics for this project, so turn off the switch and click **Create project**. If you want to use Google Analytics, leave the switch on, and there will be one extra step of choosing or creating a Google Analytics project. + +<DashImage figure src="images/0qUlFDIP4H0C4_urO.webp" /> + + +Watch for a few seconds as the project is created, until you see that it is ready and click **Continue.** + +<DashImage figure src="images/0fL_0E_cbbMff5IIr.webp" /> + + +That’s it for step 1! + +## Step 2: Install the firebase-tools CLI + +🏃‍♀Skip this step if you already have firebase-tools installed. + +If you do not have `npm`, you must install that first. + +☝️Note: If you are not sure whether `npm` is installed on your machine, run `$ npm -v` , and see if it lists a version number. If it does, then you already have `npm`. If it says “command not found”, you need to install it. + +[Installing npm on windows](https://www.guru99.com/download-install-node-js.html). + +[Installing npm on linux](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm). + +[Installing npm on mac](https://treehouse.github.io/installation-guides/mac/node-mac.html). + +Or download it directly from [Node.js](https://nodejs.org). + +Once `npm` is installed, run the following command to install the `firebase-tools`: + +``` +$ npm install -g firebase-tools +``` + + +`firebase-tools` is now installed! + +## Step 3: Initialize Firebase hosting for your Flutter project + +Open a terminal and navigate to the root directory for your Flutter app and run this command to login: + +``` +$ firebase login +``` + + +☝️Note: If you are already logged in and want to re-login as different account, first logout with: + +``` +$ firebase logout +``` + + +Follow the command prompt and the link in your browser. Accept the permissions by clicking **Ok**. When you return to your terminal you should see that you are now logged in: + +<DashImage figure src="images/0JZ_gkqDtOFpGnTtJ.jpg" /> + + +Next, run this command from the root directory of your Flutter app to initialize your Firebase project: + +``` +$ firebase init +``` + + +<DashImage figure src="images/0F4TQYa2f-9bAaW3X.webp" /> + + +Use the arrow keys to navigate the cursor to **Hosting** and hit the spacebar to select it, then press enter. Now you will see this screen: + +<DashImage figure src="images/0KnlTgdXeNMDAMpna.webp" /> + + +Select **Use an existing project** by pressing Enter. Use the arrow keys to select the project you made in step 1. + +Next, enter `build/web` as the public directory and press enter, then enter **y** (for yes) to select the single page app option: + +<DashImage figure src="images/0mYMsKK4g65KGh71f.webp" /> + + +☝️Note: After this step, 2 new files are created (`.firebaserc` and `firebase.json`) in your root directory. Check and make sure they are there. If not, check for errors in the `firebase init` step and retry. + +## Step 4: Build and deploy! + +Build your app for web: + +``` +$ flutter build web +``` + + +This builds the necessary files in `<root-directory>/build/web`. + +Note: If you see a warning: “Experimental feature web is not supported on stable branches”, make sure you are on at least the dev channel and have web enabled. For example: + +``` +$ flutter channel + beta +* dev + master + stable +$ ~ $ flutter devices +2 connected devices: + +Chrome • chrome • web-javascript • Google Chrome 78.0.3904.87 +Web Server • web-server • web-javascript • Flutter Tools +``` + + +Finally, run: + +``` +$ firebase deploy +``` + + +The hosting files are uploaded and hosted at your Firebase URL. You can follow the link in your terminal to your Flutter web project! \ No newline at end of file diff --git a/sites/www/content/blog/new-ads-beta-inline-banner-and-native-support-for-the-flutter-mobile-ads-plugin/images/10kWrLLh_g2Ywwgcs_iAL-w.webp b/sites/www/content/blog/new-ads-beta-inline-banner-and-native-support-for-the-flutter-mobile-ads-plugin/images/10kWrLLh_g2Ywwgcs_iAL-w.webp new file mode 100644 index 0000000000..e0a2589aaf Binary files /dev/null and b/sites/www/content/blog/new-ads-beta-inline-banner-and-native-support-for-the-flutter-mobile-ads-plugin/images/10kWrLLh_g2Ywwgcs_iAL-w.webp differ diff --git a/sites/www/content/blog/new-ads-beta-inline-banner-and-native-support-for-the-flutter-mobile-ads-plugin/index.md b/sites/www/content/blog/new-ads-beta-inline-banner-and-native-support-for-the-flutter-mobile-ads-plugin/index.md new file mode 100644 index 0000000000..1429ff38ce --- /dev/null +++ b/sites/www/content/blog/new-ads-beta-inline-banner-and-native-support-for-the-flutter-mobile-ads-plugin/index.md @@ -0,0 +1,34 @@ +--- +title: "New ad formats for Flutter" +description: "Beta program opens for inline banner and native ads support" +publishDate: 2020-11-11 +author: haddadniaj +image: images/10kWrLLh_g2Ywwgcs_iAL-w.webp +category: announcements +layout: blog +--- + +There are many ways that mobile app developers get paid for their work. Monetizing apps via ads is one popular path for many mobile developers, including those using Flutter. + +[Native ad format support](https://github.com/flutter/flutter/issues/12114), in particular, is one of the top five requested features for Flutter. The previously available Google ads plugin (also known as the `firebase_admob` plugin) provided by the Flutter team supported overlay banner, interstitial, and rewarded video, but couldn’t render formats such as inline banner or native ads. + +<DashImage figure src="images/10kWrLLh_g2Ywwgcs_iAL-w.webp" /> + + +## Inline Banner and Native Ads + +As the name suggests, inline banner ads let you integrate an ad unit as a part of the Flutter view, instead of overlaying the banner on top of the UI. Similarly, native ads allow you to design an ad experience that feels like a natural part of your app content. Some developers let us know that the lack of inline banner and native ads would hinder their ability to create beautiful and natural ads experiences, and others indicated that not having these features could potentially block them from adopting Flutter into their applications. + +In response to your requests, we’ve been developing support for these formats. One year ago, we started collaborating closely with Google Ads to implement a robust and trustworthy inline ads solution for Flutter. We also refactored the plugin code entirely to improve performance and stability. Today, after completing our implementation and a round of alpha testing, we are glad to announce invite-only access to a beta release of this updated plugin. + +With this beta, we built upon the existing plugin and added support for new formats across AdMob & Ad Manager. As with all beta releases, there are still kinks to work out, and feature support may change without notice. If selected for the beta, we strongly recommend that you thoroughly test the integration within your apps, before shipping to your users. + +## Apply now + +To apply for our beta test, please fill out [our application form](https://forms.gle/5d1MfrbaCLX9PS2Z9). We’re particularly looking for customers who have an existing Flutter app in the store and are willing to use our beta release in their production app, in partnership with our team. If you are accepted into the beta you will receive an email with further instructions. + +Based on the results of this beta, we expect to open this up to a broader audience in early 2021. + +We hope that inline banner and native ads help you unlock more revenue growth for your Flutter apps. + +Happy coding and advertising! \ No newline at end of file diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/0BHpYeXRNy6du98np.webp b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/0BHpYeXRNy6du98np.webp new file mode 100644 index 0000000000..0564e9ab0f Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/0BHpYeXRNy6du98np.webp differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/0JElGgGi_Kf6KM7nx.webp b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/0JElGgGi_Kf6KM7nx.webp new file mode 100644 index 0000000000..2d392a9b4c Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/0JElGgGi_Kf6KM7nx.webp differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/0VLibsxLD9Wv35Pr8.webp b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/0VLibsxLD9Wv35Pr8.webp new file mode 100644 index 0000000000..1dbcd493d3 Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/0VLibsxLD9Wv35Pr8.webp differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/0iVa4x2t2lP9ohPN4.webp b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/0iVa4x2t2lP9ohPN4.webp new file mode 100644 index 0000000000..4bda3170ac Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/0iVa4x2t2lP9ohPN4.webp differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/12trBIvd2s8ToANRxsA8FIg.webp b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/12trBIvd2s8ToANRxsA8FIg.webp new file mode 100644 index 0000000000..f9f2b6bc4b Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/12trBIvd2s8ToANRxsA8FIg.webp differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/18AbvqZly0A4dixM5yq6iQg.webp b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/18AbvqZly0A4dixM5yq6iQg.webp new file mode 100644 index 0000000000..6ec9059876 Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/18AbvqZly0A4dixM5yq6iQg.webp differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1BPb2b4wqKgGsitAaKuShUw.webp b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1BPb2b4wqKgGsitAaKuShUw.webp new file mode 100644 index 0000000000..ef060e451a Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1BPb2b4wqKgGsitAaKuShUw.webp differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1CE2_scmNy11X1Fl3zj7bGA.webp b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1CE2_scmNy11X1Fl3zj7bGA.webp new file mode 100644 index 0000000000..c5162608c6 Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1CE2_scmNy11X1Fl3zj7bGA.webp differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1KRIuvikDtgXpHSIgGe9a9Q.webp b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1KRIuvikDtgXpHSIgGe9a9Q.webp new file mode 100644 index 0000000000..e942b04304 Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1KRIuvikDtgXpHSIgGe9a9Q.webp differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1VqwAbaGamLPA-v6icQJOVQ.webp b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1VqwAbaGamLPA-v6icQJOVQ.webp new file mode 100644 index 0000000000..6f5da2871c Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1VqwAbaGamLPA-v6icQJOVQ.webp differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1Z7TXqke4-9T7EaTKgNSTsw.webp b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1Z7TXqke4-9T7EaTKgNSTsw.webp new file mode 100644 index 0000000000..f5b82ea27e Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1Z7TXqke4-9T7EaTKgNSTsw.webp differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1e2q4GYtedlU7b-TKLgnRyg.webp b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1e2q4GYtedlU7b-TKLgnRyg.webp new file mode 100644 index 0000000000..013abb07d0 Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1e2q4GYtedlU7b-TKLgnRyg.webp differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1eIyRlQ_O5COR9cGuMubUZw.webp b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1eIyRlQ_O5COR9cGuMubUZw.webp new file mode 100644 index 0000000000..03b47c3372 Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1eIyRlQ_O5COR9cGuMubUZw.webp differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1hcoGwr98wOif8v6rNJajUw.webp b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1hcoGwr98wOif8v6rNJajUw.webp new file mode 100644 index 0000000000..27de195c0c Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1hcoGwr98wOif8v6rNJajUw.webp differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1hdjm79pT22-sAwjQhFYr8A.webp b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1hdjm79pT22-sAwjQhFYr8A.webp new file mode 100644 index 0000000000..7024e446a2 Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1hdjm79pT22-sAwjQhFYr8A.webp differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1iG8tpvV34F3XOXyzLC2U0Q.webp b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1iG8tpvV34F3XOXyzLC2U0Q.webp new file mode 100644 index 0000000000..b74815a3d8 Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1iG8tpvV34F3XOXyzLC2U0Q.webp differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1s-H2qFeoTWmdEnJOhG39Vw.webp b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1s-H2qFeoTWmdEnJOhG39Vw.webp new file mode 100644 index 0000000000..3f78dd8b53 Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1s-H2qFeoTWmdEnJOhG39Vw.webp differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1sVn-oXHO2jW9sWpxG82njg.gif b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1sVn-oXHO2jW9sWpxG82njg.gif new file mode 100644 index 0000000000..551f2e37b5 Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1sVn-oXHO2jW9sWpxG82njg.gif differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1syZl4n3gicB-CknSTb6X_A.webp b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1syZl4n3gicB-CknSTb6X_A.webp new file mode 100644 index 0000000000..95d232d0fe Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1syZl4n3gicB-CknSTb6X_A.webp differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1umZpq9lqGCrOyBJiTPKHsw.gif b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1umZpq9lqGCrOyBJiTPKHsw.gif new file mode 100644 index 0000000000..fcce9bbd47 Binary files /dev/null and b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/images/1umZpq9lqGCrOyBJiTPKHsw.gif differ diff --git a/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/index.md b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/index.md new file mode 100644 index 0000000000..496f889ecb --- /dev/null +++ b/sites/www/content/blog/new-tools-for-flutter-developers-built-in-flutter/index.md @@ -0,0 +1,162 @@ +--- +title: "New tools for Flutter developers, built in Flutter" +description: "Why we rebuilt Dart DevTools from scratch in Flutter" +publishDate: 2020-06-24 +author: filiph +image: images/0VLibsxLD9Wv35Pr8.webp +category: spotlight +layout: blog +--- + +By: Filip Hracek & Chris Sells + +Today, we’re releasing a new version of Dart DevTools, our suite of tools for debugging and performance analysis for Dart and Flutter code. It has been rebuilt from scratch in Flutter. This version adds several improvements, such as updates to the performance and memory pages, and a completely new network page. + +Many people know Flutter for its hot reload feature that lets you make changes to your mobile app while it’s running. But hot reload is but one of a suite of tools that we provide to help you write, test, debug, and analyze your app. + +<DashImage figure src="images/0VLibsxLD9Wv35Pr8.webp" /> + + +Let’s first take a moment to discuss why we rebuilt DevTools in the first place. The short answer is productivity and quality. The Flutter team understands the productivity benefits of Flutter when building beautiful, high performance UIs — and we want those benefits for ourselves. This productivity has enabled us to not only rebuild DevTools but also to add big new features along the way. + +We chose to ship DevTools as a web application, since it is easy for us to integrate into the existing tooling experience across all of our target platforms and IDEs. Building with the web in mind has also enabled us to walk in the shoes of our customers; in doing so, we’ve learned about (and fixed) some performance issues, such as scrolling performance. + +We have also been evaluating the benefits of shipping DevTools as compiled desktop apps and so far the results are promising. One major benefit of choosing Flutter is that selecting a distribution model is something we can do *after* we’ve written the code, rather than the first decision we have to make. Many customers also tell us that this is an attractive capability of Flutter. + +Now that we’ve discussed a little bit about the near past and new future of DevTools, let’s dive into a quick overview of *all* of the major tools available to every Flutter developer. Because DevTools is not the only tool worthy of your attention. + +## The basics + +Of course, there’s the `flutter` tool, which runs your app in debug mode, performs hot reload, builds APKs and IPAs, and more. The `flutter` tool is written in Dart, and runs on the command line, so that you can use Flutter no matter which operating system or editor you prefer. + +<DashImage figure src="images/0iVa4x2t2lP9ohPN4.webp" /> + + +The Dart analysis server provides static analysis and intelligence in a variety of IDEs. This means that you get code completion … + +<DashImage figure src="images/0JElGgGi_Kf6KM7nx.webp" alt="This happens to be VS Code, but all the examples here work in Android Studio as well, of course." caption="This happens to be VS Code, but all the examples here work in Android Studio as well, of course." /> + + +… error highlights that link to documentation on [how to fix them](https://dart.dev/tools/diagnostic-messages) … + +<DashImage figure src="images/1KRIuvikDtgXpHSIgGe9a9Q.webp" alt="For the most common static errors, IDEs link to a longer version of the error message, including example code and common fixes." caption="For the most common static errors, IDEs link to a longer version of the error message, including example code and common fixes." /> + + +… and smart lints. + +<DashImage figure src="images/1VqwAbaGamLPA-v6icQJOVQ.webp" alt="Here, the analyzer detects that a Sink is created but is never closed — a possible memory leak." caption="Here, the analyzer detects that a Sink is created but is never closed — a possible memory leak." /> + + +The analysis server can also use the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/), which means all the developer tools that use it (and there are [many](https://microsoft.github.io/language-server-protocol/implementors/tools/)) can easily support it. + +## UI code helpers + +With Flutter, you build UI in code by creating widget trees. This means that a lot of Dart code is in the form of nested constructors like the following: + +``` +return MaterialApp( + title: 'My app', + home: Scaffold( + appBar: AppBar( + title: Text('Welcome'), + ), + body: Column( + ... + ), + ), +); +``` + + +Flutter tooling makes it easier to work with code like this. UI guides highlight the widget tree that you are building. + +<DashImage figure src="images/1iG8tpvV34F3XOXyzLC2U0Q.webp" alt="The lines on the left, called UI guides, explicitly visualize the widget tree." caption="The lines on the left, called UI guides, explicitly visualize the widget tree." /> + + +Trailing comments can show which closing bracket belongs to which widget. + +<DashImage figure src="images/1e2q4GYtedlU7b-TKLgnRyg.webp" alt="The comments here are shown by the IDE but are not actually part of the file." caption="The comments here are shown by the IDE but are not actually part of the file." /> + + +Smart refactoring helps you modify the tree in a few strokes, such as wrapping a part of it with a new parent … + +<DashImage figure src="images/1sVn-oXHO2jW9sWpxG82njg.gif" alt="Wrapping an IconButton with a SizedBox." caption="Wrapping an IconButton with a SizedBox." /> + + +… and moving widgets around. + +<DashImage figure src="images/1umZpq9lqGCrOyBJiTPKHsw.gif" /> + + +## DevTools + +And then there are DevTools, a standalone suite of tools that run in the browser. They provide additional telemetry and functionality that isn’t practical to show in the IDE. + +<DashImage figure src="images/1hcoGwr98wOif8v6rNJajUw.webp" alt="DevTools open in a browser window." caption="DevTools open in a browser window." /> + + +The DevTools we’re launching this week are written in Flutter. (They were previously written in Dart, but not using the Flutter framework.) This was a complete rewrite from scratch, which brought opportunities to improve the UI and add completely new features. + +<DashImage figure src="images/1Z7TXqke4-9T7EaTKgNSTsw.webp" /> + + +The first tab is the [Flutter Inspector](https://flutter.dev/docs/development/tools/devtools/inspector), which is a tool for visualizing and exploring Flutter widget trees. From here, you can select widgets in your running app, slow down all animations, see text baselines, and more. + +One of the new features is the Layout Explorer, which you can find in the Flutter Inspector tab next to the Details Tree. The Layout Explorer lets you inspect Flutter’s flex layout model. This can be a lifesaver when you’re unsure why a row of widgets doesn’t look the way you expected, for example, or why you’re getting the “RenderFlex overflowed by 42 pixels” error. + +<DashImage figure src="images/12trBIvd2s8ToANRxsA8FIg.webp" /> + + +Next to the Flutter Inspector tab, you’ll find three tabs dedicated to performance profiling: the Timeline view, the Memory view, and the Performance view. + +The [Timeline view](https://flutter.dev/docs/development/tools/devtools/timeline) now shows build times for each frame as well as a flame chart. This makes it easy to identify problematic frames while seeing them in context. + +<DashImage figure src="images/18AbvqZly0A4dixM5yq6iQg.webp" /> + + +The Timeline pane also has the new Track Widget Builds button, which adds build times of all widgets in your app to the timeline (at the expense of performance of your profile build — which is why it’s not on by default). This is handy when you’re trying to find out which widgets, exactly, are behind a slow frame. + +The [Memory view](https://flutter.dev/docs/development/tools/devtools/memory) lets you peek at how your app is using memory at a given moment. This view now shows a heatmap of the allocated memory, and allows tracking platform memory as well. + +<DashImage figure src="images/1syZl4n3gicB-CknSTb6X_A.webp" /> + + +The [Performance view](https://flutter.dev/docs/development/tools/devtools/performance) is a traditional CPU profiler. It lets you record a session of your app, and see in which functions the CPU spent most of its time, overall. This is generally used to decide where to spend optimization efforts. + +<DashImage figure src="images/1CE2_scmNy11X1Fl3zj7bGA.webp" /> + + +DevTools even includes its own [Debugger](https://flutter.dev/docs/development/tools/devtools/debugger). This can be useful if you are not using an IDE for development, but still want the option to add breakpoints, step through the code, peek at variable values, and so on. + +<DashImage figure src="images/1BPb2b4wqKgGsitAaKuShUw.webp" /> + + +The next tab is completely new. The [Network view](https://flutter.dev/docs/development/tools/devtools/network) lets you inspect, you guessed it, network traffic. You can see the whole history of requests that your app made since it started, and detailed information about each one. This frees you from having to log these events on your own when trying to debug a network issue. The Network tab currently shows HTTP traffic; future improvements include showing [general socket I/O traffic](https://github.com/flutter/devtools/issues/2044). + +<DashImage figure src="images/1hdjm79pT22-sAwjQhFYr8A.webp" /> + + +You’ll now also find network requests in the Timeline view, so that you can see them in context. + +The [Logging view](https://flutter.dev/docs/development/tools/devtools/logging) shows events from your app and the framework. With it, you can easily filter messages (for example, you can specify `-gc` to filter out Garbage Collector events, or `flutter.frame` to only show frame events). In Dart, logging messages [can be structured](https://api.dart.dev/stable/dart-developer/log.html), and the Logging view makes use of that. + +<DashImage figure src="images/1s-H2qFeoTWmdEnJOhG39Vw.webp" /> + + +DevTools work for mobile apps, desktop apps, *and* web apps. + +<DashImage figure src="images/1eIyRlQ_O5COR9cGuMubUZw.webp" /> + + +The rewrite of DevTools to Flutter has many benefits: increased productivity, walking in the shoes of our customers, and freedom of choice of target platforms. One benefit we haven’t mentioned: rebuilding DevTools in Flutter invites the community to contribute more easily. DevTools was always [developed in the open](https://github.com/flutter/devtools/tree/master/packages/devtools_app), but today, most users will be familiar with its structure (starting with `runApp(DevToolsApp(…)`) in [`lib/main.dart`](https://github.com/flutter/devtools/blob/master/packages/devtools_app/lib/main.dart#L14-L16)). + +<DashImage figure src="images/0BHpYeXRNy6du98np.webp" /> + + +## Summary + +Tooling is vitally important for the Flutter developer experience. We invest heavily in making it better every day. But we can only do this with your feedback. + +So upgrade to the newest DevTools today, and test it out on your app. Read the [documentation](https://flutter.dev/docs/development/tools/devtools/overview) to learn about all the hidden gems. File bugs if you find them, or vote for new features [on GitHub](https://github.com/flutter/devtools/issues). + +We hope the new DevTools make your Flutter development experience more enjoyable. \ No newline at end of file diff --git a/sites/www/content/blog/new-updates-to-a2ui-and-flutters-genui-package/images/1yPatUacknopOAVF_Vr86OA.webp b/sites/www/content/blog/new-updates-to-a2ui-and-flutters-genui-package/images/1yPatUacknopOAVF_Vr86OA.webp new file mode 100644 index 0000000000..71810c424d Binary files /dev/null and b/sites/www/content/blog/new-updates-to-a2ui-and-flutters-genui-package/images/1yPatUacknopOAVF_Vr86OA.webp differ diff --git a/sites/www/content/blog/new-updates-to-a2ui-and-flutters-genui-package/index.md b/sites/www/content/blog/new-updates-to-a2ui-and-flutters-genui-package/index.md new file mode 100644 index 0000000000..5b1364511a --- /dev/null +++ b/sites/www/content/blog/new-updates-to-a2ui-and-flutters-genui-package/index.md @@ -0,0 +1,192 @@ +--- +title: "New updates to A2UI and Flutter’s GenUI package" +description: "Generative UI, or GenUI for short, is a user experience pattern in which an agent not only generates content, but also makes decisions…" +publishDate: 2026-05-14 +author: redbrogdon +image: images/1yPatUacknopOAVF_Vr86OA.webp +layout: blog +--- + +<DashImage figure src="images/1yPatUacknopOAVF_Vr86OA.webp" /> + + +Generative UI, or GenUI for short, is a user experience pattern in which an agent not only generates content, but also makes decisions about how that content should be displayed and made interactive for the user. For Flutter developers, implementing GenUI means using A2UI, an open protocol that defines a way for agents and clients (or “renderers”) to collaborate on the composition and state of a user interface. To capitalize on this, the Flutter team built genui, a package that uses A2UI to connect with an agent and provide it with a catalog of widgets to use, and then presents those widgets to the user. + +Both the `genui` package and the A2UI protocol recently got an update! + +The latest release of `genui` introduces several architectural changes to the framework. Driven by adoption of v0.9 of the A2UI protocol, this update shifts `genui` from a “Structured Output First” philosophy in which A2UI messages were streamed through structured output APIs to a “Prompt First” approach in which agents include blocks of JSON as text in their responses. It also decouples the architecture, providing more direct control over how your application interacts with Large Language Models (LLMs). + +If you are migrating an app from v0.7.0 to v0.9.0 of the `genui` package, this guide covers the necessary steps, from dependency cleanup to wiring up your new chat loops. + +## Architecture decoupling + +In previous versions, GenUI relied on a series of classes based on `ContentGenerator`. These classes hid the details of prompt construction, LLM network calls, and response parsing. + +The latest version of package:genui removes `ContentGenerator`. Instead, the framework is now split into distinct layers: + +* **Engine** (`SurfaceController`): Manages the state and rendering of your UI. + +* **Transport (**`A2uiTransportAdapter`**)**: Streams messages between the agent and renderer. + +* **Facade** (`Conversation`): Provides a high-level API for managing chat states. + +This decoupling means you have control over chat history, retry logic, and error handling. It also means you can set up your connection to your LLM however you’d like. The framework no longer “wraps” your agent with a `ContentGenerator`, so you’re free to use the model and provider you’d prefer, tweak generation settings, add your own functions, and so on, without needing to go through the framework’s API to do so. + +Because the `ContentGenerator` is gone, the provider-specific wrapper packages are no longer needed. If you pull the latest version of the package, you’ll see that names like `genui_dartantic`, `genui_google_generative_ai`, and `genui_firebase_ai` no longer appear in the tree. + +This is the most significant code change in the migration. Instead of passing a `ContentGenerator` to your `SurfaceController`, your app is responsible for setting up a connection to an agent and passing messages back and forth through a `TransportAdapter`. + +**The old way:** + +```dart +// Create a ContentGenerator that encapsulates interaction with +// the agent. +final generator = FirebaseAiContentGenerator( + catalog: CoreCatalogItems.asCatalog(), + systemInstruction: 'You are a helpful assistant.', +); + +// Create a conversation that links the generator with a GenUiManager +// that manages surfaces, updates, and so on. +final conversation = GenUiConversation( + genUiManager: GenUiManager(catalog: catalog), + contentGenerator: generator, +); +``` + + +**The new way:** + +```dart +final catalog = BasicCatalogItems.asCatalog(); + +// Create a SurfaceController to manage the state of generated surfaces. +final surfaceController = SurfaceController(catalogs: [catalog]); + +// Created a transport adapter that routes messages from the `genui` library +// to the agent, then feeds responses back into the adapter via `addChunk`. +late final adapter = A2uiTransportAdapter( + onSend: (ChatMessage msg) async { + // Use a string buffer to prepare tokens to send to the agent. + final buffer = StringBuffer(); + + // Iterate over the messages created by the `genui` package, and + // add them as tokens to the buffer. + for (final part in msg.parts) { + if (part.isUiInteractionPart) { + buffer.write(part.asUiInteractionPart!.interaction); + } else if (part is genui.TextPart) { + buffer.write(part.text); + } + } + + // Send a content generation request to the agent, including the + // string-ified messages from `genui` + final response = await myAgentClient.sendRequest(buffer.toString()); + + // After receiving the agent's response, add it to the `genui` package's + // input stream using `addChunk`, where it will be parsed for A2UI + // messages. + adapter.addChunk(response); + }, +); +``` + + +You might look at those two examples and think, “Wait, shouldn’t API improvements mean I have to write *less* code rather than more?” It’s true that previously this bit of “wiring” code for connecting with the agent was included in the `genui` package, tucked away in a `ContentGenerator` class, but the new approach has some concrete advantages: + +* Without the requirement of a `ContentGenerator`, you can set your agent up the way you like, hold it in memory where you prefer, and manage its lifecycle. You can also use nearly any AI source you’d like without waiting for a package update with a new `ContentGenerator`. + +* You no longer have to “inject” your connection to the agent into the `genui` API. They’re loosely coupled, with just tokens moving back and forth. + +* Testing is simpler. `genui` accepts tokens directly, and they can come from an agent, or a mock agent, or a hard-coded test. + +* If you’d like to encapsulate your connection into a class, you can still do so. In fact, several of [the examples](https://github.com/flutter/genui/tree/main/examples) for `genui` take this approach. + +## Going prompt first + +In previous versions, the `genui` package relied heavily on the LLM provider’s strict API-level constraints (like “JSON Mode” or complex function-calling definitions) to force the model to output valid UI structures. The schema was passed to the LLM out-of-band through specific API parameters, and the LLM was effectively locked into a rigid structure. + +While this directed models to create predictable, well-formatted JSON, deeply nested schemas could sometimes confuse models or fight against their natural text-generation tendencies. Plus, reliance on structured outputs imposed limits on the overall size and complexity of the catalog. It also made debugging harder because the constraints lived entirely in the network layer rather than in plain text that you could easily read and tweak. + +The “prompt first” approach shifts the source of truth back to where LLMs excel: system instructions. Instead of relying entirely on strict API toggles, the UI schema and instructions relating to A2UI are injected directly into the LLM’s system prompt as plain text. The LLM reads the instructions, detailing exactly how to construct messages for the client. + +This approach has a few advantages. First, modern LLMs are highly optimized to follow detailed system instructions and examples. Providing the schema in the prompt aligns with how they “think.” In addition, because the UI schema is now just plain text in your prompt, you have the ability to change it to suit your application. + +This change means responsibility for getting the right prompt into the agent’s context window now rests with your app. Fortunately, the `genui` package provides a new tool to help you craft the right system prompt for your application, `PromptBuilder`. Given a catalog and any additional instructions you’d like to provide, `PromptBuilder` will create a system prompt that includes the schema definitions and rules that your LLM needs to format A2UI messages correctly. + +```dart +final promptBuilder = PromptBuilder.chat( + catalog: catalog, + systemPromptFragments: ['You are a helpful assistant.'], +); +``` + + +Once set up, your app can retrieve a String version of the prompt using `promptBuilder.systemPrompt`, and then pass that value to the LLM. + +## Protocol & schema adjustments + +If your code manually constructs A2UI JSON or relies on specific payload structures, note these breaking changes from the A2UI v0.9 update: + +* **Surface creation:** `beginRendering` is now `createSurface`. + +* **Flat component definitions:** Instead of nested keys (such as `{“Text”: {“text”: “Hello”}}`), components now use a flat discriminator: `{“component”: “Text”, “text”: “Hello”}`. + +* **Data binding:** Bindings are simplified. Use `{ “path”: “/path/to/var” }` for path resolution. + +**Property renames:** + +* `distribution` => `justify` + +* `alignment` => `align` + +* `usageHint` => `variant` + +* `text` (in `TextField`) => `value` + +* `userAction` => `action` + +## Other stuff got renamed! + +Along with some other small tweaks, the `GenUi` prefix has been dropped from almost all core classes: + +* `GenUiConversation` => `Conversation` + +* `GenUiController` => `SurfaceController` + +* `GenUiSurface` => `Surface` + +* `GenUiHost` => `SurfaceHost` + +* `GenUiContext` => `SurfaceContext` + +* `GenUiTransport` => `Transport` + +* `GenUiFallback` => `FallbackWidget` + +In addition, `CoreCatalogItems` was renamed to `BasicCatalogItems` to clarify that it serves as a baseline implementation rather than a strict requirement. + +Finally, to align with standard LLM function-calling terminology, `GenUiFunctionDeclaration` and references to “tools” have been renamed to `ClientFunction`. + +## The new genai_primitives package + +The `genui` package no longer ships with its own messaging types. Instead, the GenUI team created the new `genai_primitives` package with primitive types for common functionality needed by GenAI applications. This package includes types like `ChatMessage`, `MessagePart`, and `ToolDefinition`. + +These new types are used across the genui package API, and they’re flexible enough to fit into other GenAI applications or packages you might be working on. + +## And there’s more! + +In addition to the above, the latest version of A2UI and the `genui` package bring several new features like: + +* [Custom functions](https://a2ui.org/specification/v0_9/docs/a2ui_custom_functions/), useful for validating data on the client + +* A new, modular schema + +* Improved error handling + +For more information on these, check out the [v0.9 announcement blog post](https://developers.googleblog.com/a2ui-v0-9-generative-ui/) and head to [a2ui.org](https://a2ui.org). + +## Summary + +The latest update to genui introduces an architecture that is much more idiomatic, flexible, and robust. If you haven’t yet started using GenUI, there’s no better time than now! Head to our [introductory codelab](https://codelabs.developers.google.com/codelabs/genui-intro#0) to gain a hands-on understanding of the tech and create a working app in about 90 minutes. \ No newline at end of file diff --git a/sites/www/content/blog/only-a-few-days-left-to-submit-for-the-flutter-puzzle-hack-announcing-the-judges/images/0A_NEHVtXjdO7Cu0T.webp b/sites/www/content/blog/only-a-few-days-left-to-submit-for-the-flutter-puzzle-hack-announcing-the-judges/images/0A_NEHVtXjdO7Cu0T.webp new file mode 100644 index 0000000000..ae59ea7b15 Binary files /dev/null and b/sites/www/content/blog/only-a-few-days-left-to-submit-for-the-flutter-puzzle-hack-announcing-the-judges/images/0A_NEHVtXjdO7Cu0T.webp differ diff --git a/sites/www/content/blog/only-a-few-days-left-to-submit-for-the-flutter-puzzle-hack-announcing-the-judges/images/0LgF82TK9xt1aI14Y.webp b/sites/www/content/blog/only-a-few-days-left-to-submit-for-the-flutter-puzzle-hack-announcing-the-judges/images/0LgF82TK9xt1aI14Y.webp new file mode 100644 index 0000000000..d7a124ed75 Binary files /dev/null and b/sites/www/content/blog/only-a-few-days-left-to-submit-for-the-flutter-puzzle-hack-announcing-the-judges/images/0LgF82TK9xt1aI14Y.webp differ diff --git a/sites/www/content/blog/only-a-few-days-left-to-submit-for-the-flutter-puzzle-hack-announcing-the-judges/index.md b/sites/www/content/blog/only-a-few-days-left-to-submit-for-the-flutter-puzzle-hack-announcing-the-judges/index.md new file mode 100644 index 0000000000..d3a46df722 --- /dev/null +++ b/sites/www/content/blog/only-a-few-days-left-to-submit-for-the-flutter-puzzle-hack-announcing-the-judges/index.md @@ -0,0 +1,61 @@ +--- +title: "Only a few days left to submit for the Flutter Puzzle Hack + announcing the judges" +description: "Get your project in before the submission deadline!" +publishDate: 2022-03-09 +author: kkboateng +image: images/0LgF82TK9xt1aI14Y.webp +category: events +layout: blog +--- + +<DashImage figure src="images/0LgF82TK9xt1aI14Y.webp" /> + + +There are only a few days left to submit to the [Flutter Puzzle Hack](https://flutterhack.devpost.com/). We’re excited by the puzzles we’ve seen so far, and can’t wait to see even more! Don’t forget that prizes include 4 fully loaded MacBooks, tons of Flutter swag, and cash to over 40 winners, so be sure to get your submission in for a chance at some of these awesome prizes. + +## **Welcome to the Flutter community** + +With the final submission date approaching so quickly, we wanted to take a moment to welcome many new developers to the Flutter [community](https://flutter.dev/community). We can’t wait to see what you build, starting, of course, with your Flutter Puzzle Hack submission! + +## **Announcing our judges** + +In honor of the final week of the Flutter Puzzle Hack, we’re excited to announce our judges, including many Flutter veterans who helped to build the framework since its inception. A puzzle hack submission is an amazing opportunity to have your work reviewed by some of the world’s foremost Flutter experts. + +* Justin McCandless, Software Engineer, Flutter Framework + +* Casey Hillers, Software Engineer, Flutter Engineering + +* Christian Padilla, Software Engineer, Flutter Plugins + +* Devon Carew, Software Engineer, Flutter + +* Greg Spencer, Software Engineer, Flutter Framework / Desktop + +* Michael Goderbauer, Lead Software Engineer, Flutter Framework + +* Craig Labenz, Developer Relations Engineer, Flutter + +* Yusuf Mohsinally, Software Engineer, Flutter Engineering + +* Laura Willis , Product Marketing Lead, Flutter & Firebase + +* Jay Chang, Product Marketing Manager, Flutter + +* Kelvin Boateng, Product Marketing Manager, Flutter + +* Huan Lin, Software Engineer, Flutter on iOS + +* Eric Windmill, Developer Relations Engineer, Firebase and Flutter + +## **Tell us when you submit** + +Whether you’re still working out your idea, or you’ve already submitted your app, tag us on Twitter with the hashtag [#FlutterPuzzleHack](https://twitter.com/hashtag/flutterpuzzlehack) where we’ll be sure to share your work with the Flutter community. + +Check out what Twitter user [Roaa](https://twitter.com/roaakdm/status/1493614287515967492) is working on, for [example](https://twitter.com/timsneath/status/1493626374610845696?s=20&t=q7LNbDwEVprqvF9MdRozKg): + +<DashImage figure src="images/0A_NEHVtXjdO7Cu0T.webp" /> + + +## **There’s still time!** + +If you’re still thinking about starting a submission, it’s not too late. Check out the starter [sample app](https://flutter.github.io/samples/slide_puzzle.html), the [blog post](https://medium.com/flutter/how-to-build-your-own-flutter-slide-puzzle-93721ac5fad2) explaining how the app works, and then head over to [flutterhack.devpost.com](https://flutterhack.devpost.com/) to check out the rules for the challenge. \ No newline at end of file diff --git a/sites/www/content/blog/optimizing-performance-in-flutter-web-apps-with-tree-shaking-and-deferred-loading/images/0Z8CmvWbWVXUb9Wy4.webp b/sites/www/content/blog/optimizing-performance-in-flutter-web-apps-with-tree-shaking-and-deferred-loading/images/0Z8CmvWbWVXUb9Wy4.webp new file mode 100644 index 0000000000..ed6dcf0544 Binary files /dev/null and b/sites/www/content/blog/optimizing-performance-in-flutter-web-apps-with-tree-shaking-and-deferred-loading/images/0Z8CmvWbWVXUb9Wy4.webp differ diff --git a/sites/www/content/blog/optimizing-performance-in-flutter-web-apps-with-tree-shaking-and-deferred-loading/index.md b/sites/www/content/blog/optimizing-performance-in-flutter-web-apps-with-tree-shaking-and-deferred-loading/index.md new file mode 100644 index 0000000000..9ef6398916 --- /dev/null +++ b/sites/www/content/blog/optimizing-performance-in-flutter-web-apps-with-tree-shaking-and-deferred-loading/index.md @@ -0,0 +1,111 @@ +--- +title: "Optimizing performance in Flutter web apps with tree shaking and deferred loading" +description: "For the best user experience it is important that an app loads fast. The initial load time of a Flutter web application can be improved by…" +publishDate: 2020-05-19 +author: perclasson +image: images/0Z8CmvWbWVXUb9Wy4.webp +category: tutorial +layout: blog +--- + +For the best user experience it is important that an app loads fast. The initial load time of a Flutter web application can be improved by minimizing its JavaScript bundle. The Dart compiler includes features such as tree shaking and deferred loading, both of which minimize the JavaScript bundle. This article explains how they work and how you can use them in your application. + +## Tree shaking by default + +When compiling a Flutter web application, the JavaScript bundle is generated by the [`dart2js`](https://dart.dev/tools/dart2js) compiler. A release build has the [highest level of optimization](https://github.com/flutter/flutter/blob/7b1d24216e00eaa58ca04cc2d8911329fea991be/packages/flutter_tools/lib/src/build_system/targets/web.dart#L200), which includes tree shaking your code. + +[Tree shaking](https://en.wikipedia.org/wiki/Tree_shaking) is the process of eliminating dead code, by only including code that is guaranteed to be executed. This means that you do not need to worry about the size of your app’s included libraries because unused classes or functions are excluded from the compiled JavaScript bundle. + +To see tree shaking in action: + +1. Create a Dart file `greeter.dart`: + +``` +abstract class Greeter { + String greet(String name); +} + +class EnglishGreeter implements Greeter { + String greet(String name) => 'Hello $name!'; +} + +class SwedishGreeter implements Greeter { + String greet(String name) => 'Hej $name!'; +} + +void main() { + print(EnglishGreeter().greet('World')); +} +``` + + +2. Run `dart2js -O4 greeter.dart` in your terminal and take a look at the generated output `out.js`. + +In the generated JavaScript code, there aren’t any references to the `SwedishGreeter` class, or any inclusion of the string `Hej $name`, as it was removed during tree shaking by the compiler. + +The compiler can only figure out what code is reachable, and what is dead code, with static analysis. Take the following example, where the greeter is defined depending on the system locale: + +``` +Locale locale = Localizations.localeOf(context); +if (locale.languageCode == 'sv') { + greeter = SwedishGreeter(); +} else { + greeter = EnglishGreeter(); +} +``` + + +The compiler doesn’t know the user’s system locale, therefore both `EnglishGreeter` and `SwedishGreeter` are included in the JavaScript bundle. For such use cases deferred loading can help in minimizing the initial bundle size. + +## Only load code when needed with deferred loading + +[Deferred loading](https://dart.dev/guides/language/language-tour#deferred-loading), also called lazy loading, allows you to load libraries if and when needed. It can be used to load rarely-used functionality of an application. Please note that deferred loading is a `dart2js` feature, so this is not available for Flutter mobile applications. In the simplest case, mark an imported package or file as `deferred` and wait for it to load before using it: + +``` +import 'greeter.dart' deferred as greeter; + +void main() async { + await greeter.loadLibrary(); + runApp(App(title: greeter.EnglishGreeter().greet('World'))); +} +``` + + +Compiling this code generates two JavaScript files. When `loadLibrary` is called on the deferred import, it loads the `greeter` library. + +In Flutter, where everything is a widget, you might want to make use of the [`FutureBuilder`](https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html). A widget’s build method is expected to be synchronous, therefore you can’t call `await` on `loadLibrary` inside of a build method. However, you can return a `FutureBuilder` in a build method, and you can also use it to show a different UI while the library is loading: + +``` +import 'greeter.dart' deferred as greeter; + +FutureBuilder( + future: greeter.loadLibrary(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return Text(greeter.greet('World')); + } else { + return Text('Loading...'); + } + }, +) +``` + + +To try it yourself (see a [full example on GitHub](https://github.com/perclasson/flutter_code/tree/master/deferred_loading)), open Chrome DevTools and click the [**Network** tab](https://developers.google.com/web/tools/chrome-devtools/network) to inspect network activity. Reload the page to see when the library is loaded and imported. In the following screenshot, loading the `main.dart.js_1.part.js` file is deferred: + +<DashImage figure src="images/0Z8CmvWbWVXUb9Wy4.webp" /> + + +## Deferred loading of localizations in the Flutter Gallery + +The [Flutter Gallery](http://gallery.flutter.dev/) supports over 70 languages, but most users only use one. Deferring the loading of the localization strings is a great use of this feature. For example, after implementing deferred loading of localization strings in Flutter Gallery, the app’s initial JavaScript bundle size was cut in half. If you have a lot of localization strings in your Flutter web application, consider deferring the loading of those files. The [gen_l10n.dart script](https://github.com/flutter/flutter/blob/master/dev/tools/localization/bin/gen_l10n.dart) includes the flag `--use-deferred-loading` for this purpose (currently only available on the 1.19 SDK master channel). + +This post is a part of a series about what we learned when improving performance for the [Flutter Gallery](https://gallery.flutter.dev/#/). I hope you found it useful and that you learned something that you can apply to your Flutter web application! + +This post is a part of a series about what we learned when improving performance for the Flutter Gallery. Articles in the *Creating performant Flutter web apps* series: + +* Optimizing performance in Flutter web apps with tree shaking and deferred loading (this article) + +* [Improving perceived performance with image placeholders, precaching, and disabled navigation transitions](https://medium.com/flutter/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation-6b3601087a2b) + +* [Building performant Flutter widgets](https://medium.com/flutter/building-performant-flutter-widgets-3b2558aa08fa) \ No newline at end of file diff --git a/sites/www/content/blog/out-of-depth-with-flutter/images/1-1gqkQbEiJl2PKTlRBmYrg.webp b/sites/www/content/blog/out-of-depth-with-flutter/images/1-1gqkQbEiJl2PKTlRBmYrg.webp new file mode 100644 index 0000000000..575e71654d Binary files /dev/null and b/sites/www/content/blog/out-of-depth-with-flutter/images/1-1gqkQbEiJl2PKTlRBmYrg.webp differ diff --git a/sites/www/content/blog/out-of-depth-with-flutter/images/1RNOof30wEFXsbUsQ0Jof7Q.webp b/sites/www/content/blog/out-of-depth-with-flutter/images/1RNOof30wEFXsbUsQ0Jof7Q.webp new file mode 100644 index 0000000000..f43e5a2652 Binary files /dev/null and b/sites/www/content/blog/out-of-depth-with-flutter/images/1RNOof30wEFXsbUsQ0Jof7Q.webp differ diff --git a/sites/www/content/blog/out-of-depth-with-flutter/index.md b/sites/www/content/blog/out-of-depth-with-flutter/index.md new file mode 100644 index 0000000000..d6a7c5f6ac --- /dev/null +++ b/sites/www/content/blog/out-of-depth-with-flutter/index.md @@ -0,0 +1,779 @@ +--- +title: "Out of Depth with Flutter" +description: "How reactive programming, widget composition, and UI-as-code might bury your view logic in deep nesting, and what you can do about it." +publishDate: 2018-07-10 +author: mravn +image: images/1RNOof30wEFXsbUsQ0Jof7Q.webp +category: tutorial +layout: blog +--- + +[Flutter](https://flutter.io/) is a new framework for building high-quality native apps for iOS and Android in record time. In my experience using Flutter (as a member of the Flutter team), development speed is achieved primarily through the following: + +* **Stateful hot reload**. The Flutter development experience is powered by Dart compiler/VM technology, allowing you to load code changes into a running app while retaining the app state (including the place you’ve navigated to). Hit save and you’ll see the effect of the change on device in less than a second. + +* **Reactive programming**. Flutter follows other modern frameworks in its approach to defining and updating the user interface: both happen based on a single description of how the interface depends on current state. + +* **Composition**. In Flutter, everything is a widget, and you achieve any desired outcome by freely composing focused widgets, Lego brick style. + +* **UI as code**. Flutter does not come with a separate layout markup language. Each widget is written in Dart only, in one place, eliminating syntax switching and file switching overhead. + +Interestingly, the last three bullets above conspire to form a challenge to development speed: *losing your way and your view logic inside deeply nested widget trees*. + +Below I’ll discuss why this issue arises and what you can do about it. Along the way I’ll try to shed some light on how Flutter works. + +## Reactive programming + +Flutter’s reactive programming model invites you to use declarative programming to define your user interface, as a function of current state: + +``` +@override +Widget build(BuildContext context) { + **return** // *some widget based on current state* +} +``` + + +Widgets are immutable descriptions of user interface. We are asked to return a single widget defined by a single expression. There is no sequence of mutator commands to configure or update a mutable view. Instead, we just call some widget constructor. + +## Composition + +Widgets are typically simple, each doing one thing well: `Text`, `Icon`, `Padding`, `Center`, `Column`, `Row`, … To achieve any non-trivial outcome, many widgets must be composed. So our single expression easily becomes a deeply nested tree of widget constructor calls: + +<DashImage figure src="images/1RNOof30wEFXsbUsQ0Jof7Q.webp" /> + + +Widgets have properties other than child/children, but you get the idea. + +## UI as code + +Writing and editing deeply nested trees requires a decent editor and a bit of practice to become efficient. Developers seem to tolerate deep nesting better in layout markup (XML, HTML) than in code, but Flutter’s UI-as-code approach does mean deeply nested *code*. Whatever view logic you might have inside your widget tree — conditionals, transformations, iterations used while reading current state, event handlers used for changing it — also gets deeply nested. + +And that, then, is the challenge. + +## The challenge + +The flutter.io [layout tutorial](https://flutter.io/tutorials/layout/) provides an illustrative example using — so it seems — a lake explorer app. + +<DashImage figure src="images/1-1gqkQbEiJl2PKTlRBmYrg.webp" /> + + +Here is a raw widget tree implementing this view: + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + home: Scaffold( + appBar: AppBar(title: Text('Top Lakes')), + body: ListView( + children: <Widget>[ + Image.asset( + 'images/lake.jpg', + width: 600.0, + height: 240.0, + fit: BoxFit.cover, + ), + Container( + padding: const EdgeInsets.all(32.0), + child: Row( + children: <Widget>[ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: <Widget>[ + Container( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + 'Oeschinen Lake Campground', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + Text( + 'Kandersteg, Switzerland', + style: TextStyle(color: Colors.grey[500]), + ), + ], + ), + ), + Row( + children: <Widget>[ + Icon(Icons.star, color: Colors.red[500]), + Text('41'), + ], + ), + ], + ), + ), + Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: <Widget>[ + Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + Icon(Icons.call, color: Theme.of(context).primaryColor), + Container( + margin: const EdgeInsets.only(top: 8.0), + child: Text( + 'CALL', + style: TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.w400, + color: Theme.of(context).primaryColor, + ), + ), + ), + ], + ), + Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + Icon(Icons.near_me, + color: Theme.of(context).primaryColor), + Container( + margin: const EdgeInsets.only(top: 8.0), + child: Text( + 'ROUTE', + style: TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.w400, + color: Theme.of(context).primaryColor, + ), + ), + ), + ], + ), + Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + Icon(Icons.share, color: Theme.of(context).primaryColor), + Container( + margin: const EdgeInsets.only(top: 8.0), + child: Text( + 'SHARE', + style: TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.w400, + color: Theme.of(context).primaryColor, + ), + ), + ), + ], + ), + ], + ), + ), + Container( + padding: const EdgeInsets.all(32.0), + child: Text( + 'Lake Oeschinen lies at the foot of the Blüemlisalp in the ' + 'Bernese Alps. Situated 1,578 meters above sea level, it ' + 'is one of the larger Alpine Lakes. A gondola ride from ' + 'Kandersteg, followed by a half-hour walk through pastures ' + 'and pine forest, leads you to the lake, which warms to ' + '20 degrees Celsius in the summer. Activities enjoyed here ' + 'include rowing, and riding the summer toboggan run.', + softWrap: true, + ), + ), + ], + ), + ), + ); + } +} + +``` + +This is just a static widget tree, implementing no behavior. But embedding your view logic directly into such a tree might not be a pleasant experience. + +Challenge accepted. + +## UI as code, revisited + +With Flutter’s UI-as-code approach, the widget tree is, well, just code. So we can employ all of our usual code organizing tools to improve the situation. One of the simplest tools in the box is naming subexpressions. This turns the widget tree inside out, syntactically. Instead of + +``` +**return** A(B(C(D(), E())), F()); +``` + + +we might name every subexpression and get + +``` +**final** Widget d = D(); +**final** Widget e = E(); +**final** Widget c = C(d, e); +**final** Widget b = B(c); +**final** Widget f = F(); +**return** A(b, f); +``` + + +Our lake app can be rewritten as follows: + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + final Widget imageSection = Image.asset( + 'images/lake.jpg', + width: 600.0, + height: 240.0, + fit: BoxFit.cover, + ); + final Widget titles = Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: <Widget>[ + Container( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + 'Oeschinen Lake Campground', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + Text( + 'Kandersteg, Switzerland', + style: TextStyle(color: Colors.grey[500]), + ), + ], + ); + final Widget stars = Row( + children: <Widget>[ + Icon(Icons.star, color: Colors.red[500]), + Text('41'), + ], + ); + final Widget titleSection = Container( + padding: const EdgeInsets.all(32.0), + child: Row( + children: <Widget>[ + Expanded(child: titles), + stars, + ], + ), + ); + final Widget callAction = Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + Icon(Icons.call, color: Theme.of(context).primaryColor), + Container( + margin: const EdgeInsets.only(top: 8.0), + child: Text( + 'CALL', + style: TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.w400, + color: Theme.of(context).primaryColor, + ), + ), + ), + ], + ); + final Widget routeAction = Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + Icon(Icons.near_me, color: Theme.of(context).primaryColor), + Container( + margin: const EdgeInsets.only(top: 8.0), + child: Text( + 'ROUTE', + style: TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.w400, + color: Theme.of(context).primaryColor, + ), + ), + ), + ], + ); + final Widget shareAction = Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + Icon(Icons.share, color: Theme.of(context).primaryColor), + Container( + margin: const EdgeInsets.only(top: 8.0), + child: Text( + 'SHARE', + style: TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.w400, + color: Theme.of(context).primaryColor, + ), + ), + ), + ], + ); + final Widget actionSection = Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: <Widget>[ + callAction, + routeAction, + shareAction, + ], + ), + ); + final Widget textSection = Container( + padding: const EdgeInsets.all(32.0), + child: Text( + 'Lake Oeschinen lies at the foot of the Blüemlisalp in the ' + 'Bernese Alps. Situated 1,578 meters above sea level, it ' + 'is one of the larger Alpine Lakes. A gondola ride from ' + 'Kandersteg, followed by a half-hour walk through pastures ' + 'and pine forest, leads you to the lake, which warms to ' + '20 degrees Celsius in the summer. Activities enjoyed here ' + 'include rowing, and riding the summer toboggan run.', + softWrap: true, + ), + ); + final Widget scaffold = Scaffold( + appBar: AppBar(title: Text('Top Lakes')), + body: ListView( + children: <Widget>[ + imageSection, + titleSection, + actionSection, + textSection, + ], + ), + ); + return MaterialApp( + title: 'Flutter Demo', + home: scaffold, + ); + } +} +``` + +The indentation levels are now more reasonable, and we can make the subtrees as shallow as we want by introducing more names. Even better, by giving meaningful names to individual subtrees, we communicate the role of each. So we can now talk about the `xxxAction` subtrees… and observe that we have a lot of code duplication across those! Another basic code organizing tool — functional abstraction — takes care of that: + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + final Widget imageSection = ... + final Widget titles = ... + final Widget stars = ... + final Widget titleSection = ... + + Widget action(String label, IconData icon) { + final Color color = Theme.of(context).primaryColor; + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + Icon(icon, color: color), + Container( + margin: const EdgeInsets.only(top: 8.0), + child: Text( + label, + style: TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.w400, + color: color, + ), + ), + ), + ], + ); + } + + final Widget actionSection = Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: <Widget>[ + action('CALL', Icons.call), + action('ROUTE', Icons.near_me), + action('SHARE', Icons.share), + ], + ), + ); + final Widget textSection = ... + final Widget scaffold = ... + return MaterialApp( + title: 'Flutter Demo', + home: scaffold, + ); + } +} + +``` + +We’ll see a more Fluttery alternative to plain functional abstraction in a bit. + +## Composition, revisited + +What next? Well, the `build` method is still rather long. Maybe we can extract some meaningful pieces… Pieces? Widgets! Flutter widgets are all about composition and reuse. We have composed a complex widget from simple ones provided by the framework. But finding the result too complex, we can opt to decompose it into less complex, custom widgets. Custom widgets are first-class citizens in the Flutter world, and sharply defined widgets have great reuse potential. Let’s turn the `action` function into an `Action` widget type and place it in a file of its own: + +```dart +import 'package:flutter/material.dart'; +import 'src/widgets.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + final Widget imageSection = ... + final Widget titles = ... + final Widget titleSection = ... + final Widget actionSection = Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: <Widget>[ + Action(label: 'CALL', icon: Icons.call), + Action(label: 'ROUTE', icon: Icons.near_me), + Action(label: 'SHARE', icon: Icons.share), + ], + ), + ); + final Widget textSection = ... + final Widget scaffold = ... + return MaterialApp( + title: 'Flutter Demo', + home: scaffold, + ); + } +} +``` + +```dart +import 'package:flutter/material.dart'; + +class Action extends StatelessWidget { + Action({Key key, this.label, this.icon}) : super(key: key); + + final String label; + final IconData icon; + + @override + Widget build(BuildContext context) { + final Color color = Theme.of(context).primaryColor; + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + Icon(icon, color: color), + Container( + margin: const EdgeInsets.only(top: 8.0), + child: Text( + label, + style: TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.w400, + color: color, + ), + ), + ), + ], + ); + } +} +``` + +Now we can reuse the `Action` widget anywhere in our app, precisely as if it was defined by the Flutter framework. + +But hey, wouldn’t a top-level `action` function serve the same need? + +In general, no. + +* Many widgets are constructed from other widgets; their constructors have `child` and `children` parameters typed `Widget` and `List<Widget>`. So the `action` function cannot be passed to any of those. Of course, the result of *invoking* `action` can. But then you would pass a widget tree pre-built in the current build context, rather than a `StatelessWidget` that builds its subtree only if needed and in the context defined by wherever it ends up in the overall tree. Noticed the expression `Theme.of(context).primaryColor` at the start of `Action.build`? It retrieves the primary color from the nearest `Theme` widget up the parent chain — which may well be different from the nearest `Theme` at the point where `action` would be invoked. + +* `Action` is defined as a `StatelessWidget` which is little more than a build function turned into an instance method. But there are other kinds of widget with more elaborate behavior. Clients of `Action` shouldn’t care what kind of widget `Action` is. As an example, if we wanted to endow `Action` with an intrinsic animation, we might have to turn it into a `StatefulWidget` to manage the animation state. The rest of the app should be unaffected by such a change. + +## Reactive programming, revisited + +State management is the cue to start taking advantage of Flutter’s reactive programming model and make our static view come alive. Let’s define the state of the app. We’ll keep it simple, and assume a `Lake` business logic class whose only mutable state is whether the user has starred it: + +``` +**abstract class** Lake { + String **get** imageAsset; + String **get** name; + String **get** locationName; + String **get** description; + + int get starCount; + bool get isStarred; + void toggleStarring(); + + void call(); + void route(); + void share(); +} +``` + + +We can then construct our widget tree dynamically from a `Lake` instance and, as part of that, set up event handlers to call its methods. The beauty of the reactive programming model is that we only have to do this once in the code base. The Flutter framework will rebuild our widget tree whenever the `Lake` instance changes — provided we tell the framework about it. That requires making `MyApp` a `StatefulWidget` which in turn involves delegating widget building to an associated `State` object and then calling the `State.setState` method whenever we toggle starring on our `Lake`. + +```dart +import 'package:flutter/material.dart'; +import 'src/lake.dart'; +import 'src/widgets.dart'; + +void main() { + // Pretend we get a Lake instance from business logic. + final Lake lake = Lake(); + runApp(MyApp(lake)); +} + +class MyApp extends StatefulWidget { + final Lake lake; + + MyApp(this.lake); + + @override + MyAppState createState() => new MyAppState(); +} + +class MyAppState extends State<MyApp> { + @override + Widget build(BuildContext context) { + final Lake lake = widget.lake; + final Widget imageSection = Image.asset( + lake.imageAsset, + width: 600.0, + height: 240.0, + fit: BoxFit.cover, + ); + final Widget titles = Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: <Widget>[ + Container( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + lake.name, + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + Text( + lake.locationName, + style: TextStyle(color: Colors.grey[500]), + ), + ], + ); + final Widget stars = GestureDetector( + child: Row( + children: <Widget>[ + Icon( + lake.isStarred ? Icons.star : Icons.star_border, + color: Colors.red[500], + ), + Text('${lake.starCount}'), + ], + ), + onTap: () { + setState(() { + lake.toggleStarring(); + }); + }, + ); + final Widget titleSection = Container( + padding: const EdgeInsets.all(32.0), + child: Row( + children: <Widget>[ + Expanded(child: titles), + stars, + ], + ), + ); + final Widget actionSection = Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: <Widget>[ + Action(label: 'CALL', icon: Icons.call, handler: lake.call), + Action(label: 'ROUTE', icon: Icons.near_me, handler: lake.route), + Action(label: 'SHARE', icon: Icons.share, handler: lake.share), + ], + ), + ); + final Widget textSection = Container( + padding: const EdgeInsets.all(32.0), + child: Text( + lake.description, + softWrap: true, + ), + ); + final Widget scaffold = Scaffold( + appBar: AppBar(title: Text('Top Lakes')), + body: ListView( + children: <Widget>[ + imageSection, + titleSection, + actionSection, + textSection, + ], + ), + ); + return MaterialApp( + title: 'Flutter Demo', + home: scaffold, + ); + } +} + +``` + +```dart +import 'package:flutter/material.dart'; + +class Action extends StatelessWidget { + Action({Key key, this.label, this.icon, this.handler}) : super(key: key); + + final String label; + final IconData icon; + final VoidCallback handler; + + @override + Widget build(BuildContext context) { + final Color color = Theme.of(context).primaryColor; + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + IconButton( + icon: Icon(icon, color: color), + onPressed: handler, + ), + Text( + label, + style: TextStyle( + fontSize: 12.0, + fontWeight: FontWeight.w400, + color: color, + ), + ), + ], + ); + } +} + +``` + +This works, but is not particularly efficient. The original challenge was a deeply nested widget tree. That tree is still there, if not in our code, then at runtime. Rebuilding all of it just to toggle starring is wasteful. Granted, Dart is implemented to handle short-lived objects pretty efficiently, but even Dart will eat your battery, if you repeatedly rebuild the world — especially where animations are involved. In general, we should confine rebuilding to the subtrees that actually change. + +Did you catch the contradiction? The widget tree is an immutable description of the user interface. How can we rebuild part of that without reconstructing it from the root? Well, in truth, the widget tree is not a materialized tree structure with references from parent widget to child widget, root to leaf. In particular, `StatelessWidget` and `StatefulWidget` don’t have child references. What they do provide are `build` methods (in the stateful case, via the associated `State` instances). The Flutter framework calls those `build` methods, recursively, while generating or updating an actual runtime tree structure, not of widgets, but of `Element` instances referring to widgets. The element tree is mutable, and managed by the Flutter framework. + +So what actually happens when you call `setState` on a `State` instance *s*? The Flutter framework marks the subtree rooted at the element corresponding to *s* for a rebuild. When the next frame is due, that subtree is updated based on the widget tree returned by the `build` method of *s*, which in turn depends on current app state. + +Our final stab at the code extracts a stateful `LakeStars` widget to confine rebuilds to a very small subtree. And `MyApp` is back to being stateless: + +```dart +import 'package:flutter/material.dart'; +import 'src/lake.dart'; +import 'src/widgets.dart'; + +void main() { + // Pretend we get a Lake instance from business logic. + final Lake lake = Lake(); + runApp(MyApp(lake)); +} + +class MyApp extends StatelessWidget { + const MyApp(this.lake); + + final Lake lake; + + @override + Widget build(BuildContext context) { + final Widget imageSection = ... + final Widget titles = ... + final Widget titleSection = Container( + padding: const EdgeInsets.all(32.0), + child: Row( + children: <Widget>[ + Expanded(child: titles), + LakeStars(lake: lake), + ], + ), + ); + final Widget actionSection = ... + final Widget textSection = ... + final Widget scaffold = ... + return MaterialApp( + title: 'Flutter Demo', + home: scaffold, + ); + } +} + +``` + +```dart +import 'package:flutter/material.dart'; +import 'lake.dart'; + +class LakeStars extends StatefulWidget { + LakeStars({Key key, this.lake}) : super(key: key); + + final Lake lake; + + @override + State createState() => LakeStarsState(); +} + +class LakeStarsState extends State<LakeStars> { + @override + Widget build(BuildContext context) { + final Lake lake = widget.lake; + return GestureDetector( + child: Row( + children: <Widget>[ + Icon( + lake.isStarred ? Icons.star : Icons.star_border, + color: Colors.red[500], + ), + Text('${lake.starCount}'), + ], + ), + onTap: () { + setState(() { + lake.toggleStarring(); + }); + }, + ); + } +} + +class Action extends StatelessWidget { ... } + +``` + +It seems sensible to decouple a generally applicable `Stars` widget from the `Lake` concept, but I’ll leave that as an exercise for the reader. + +Having successfully added view logic to the code, at manageable nesting depth, I think we have arrived at a reasonable response to the deep nesting challenge. + +One could imagine several interesting technical solutions to the problem of losing track of your Flutter view logic inside deeply nested widget trees. Some of them might require changes to the Flutter framework, to IDEs and other tooling, or maybe even to Dart language syntax. + +But there are powerful things you can do already today, simply by turning the causes of the problem — UI as code, widget composition, and reactive programming — to your advantage. Getting rid of deeply nested syntax is only the beginning of a journey towards readable, maintainable, and efficient mobile app code. + +Happy Fluttering! \ No newline at end of file diff --git a/sites/www/content/blog/performance-testing-of-flutter-apps/images/00S0itDkQHzRjcY1N.webp b/sites/www/content/blog/performance-testing-of-flutter-apps/images/00S0itDkQHzRjcY1N.webp new file mode 100644 index 0000000000..c7b7f6c69d Binary files /dev/null and b/sites/www/content/blog/performance-testing-of-flutter-apps/images/00S0itDkQHzRjcY1N.webp differ diff --git a/sites/www/content/blog/performance-testing-of-flutter-apps/images/0PVigpdSw-WlNfJHI.webp b/sites/www/content/blog/performance-testing-of-flutter-apps/images/0PVigpdSw-WlNfJHI.webp new file mode 100644 index 0000000000..3602dc68c3 Binary files /dev/null and b/sites/www/content/blog/performance-testing-of-flutter-apps/images/0PVigpdSw-WlNfJHI.webp differ diff --git a/sites/www/content/blog/performance-testing-of-flutter-apps/images/0ewPiWp1FMME8HDnV.webp b/sites/www/content/blog/performance-testing-of-flutter-apps/images/0ewPiWp1FMME8HDnV.webp new file mode 100644 index 0000000000..638618e689 Binary files /dev/null and b/sites/www/content/blog/performance-testing-of-flutter-apps/images/0ewPiWp1FMME8HDnV.webp differ diff --git a/sites/www/content/blog/performance-testing-of-flutter-apps/images/0vJSgoS61dEqAK7kf.webp b/sites/www/content/blog/performance-testing-of-flutter-apps/images/0vJSgoS61dEqAK7kf.webp new file mode 100644 index 0000000000..0bcfd4b218 Binary files /dev/null and b/sites/www/content/blog/performance-testing-of-flutter-apps/images/0vJSgoS61dEqAK7kf.webp differ diff --git a/sites/www/content/blog/performance-testing-of-flutter-apps/images/1MxtHyEfdBlANwu7ZRyX2mw.webp b/sites/www/content/blog/performance-testing-of-flutter-apps/images/1MxtHyEfdBlANwu7ZRyX2mw.webp new file mode 100644 index 0000000000..b49d9ec5dc Binary files /dev/null and b/sites/www/content/blog/performance-testing-of-flutter-apps/images/1MxtHyEfdBlANwu7ZRyX2mw.webp differ diff --git a/sites/www/content/blog/performance-testing-of-flutter-apps/images/1Y7-lpotj3tTjqJyobYyKHw.webp b/sites/www/content/blog/performance-testing-of-flutter-apps/images/1Y7-lpotj3tTjqJyobYyKHw.webp new file mode 100644 index 0000000000..403c99e84a Binary files /dev/null and b/sites/www/content/blog/performance-testing-of-flutter-apps/images/1Y7-lpotj3tTjqJyobYyKHw.webp differ diff --git a/sites/www/content/blog/performance-testing-of-flutter-apps/images/1ynM53tboJVQFcpmxRuuu4A.webp b/sites/www/content/blog/performance-testing-of-flutter-apps/images/1ynM53tboJVQFcpmxRuuu4A.webp new file mode 100644 index 0000000000..b405348af0 Binary files /dev/null and b/sites/www/content/blog/performance-testing-of-flutter-apps/images/1ynM53tboJVQFcpmxRuuu4A.webp differ diff --git a/sites/www/content/blog/performance-testing-of-flutter-apps/index.md b/sites/www/content/blog/performance-testing-of-flutter-apps/index.md new file mode 100644 index 0000000000..87e47a5e96 --- /dev/null +++ b/sites/www/content/blog/performance-testing-of-flutter-apps/index.md @@ -0,0 +1,153 @@ +--- +title: "Performance Testing of Flutter apps" +description: "The Flutter framework does a good job being fast by default, but does that mean you don’t need to think about performance at all? No. It…" +publishDate: 2019-05-09 +author: filiph +image: images/1MxtHyEfdBlANwu7ZRyX2mw.webp +category: tutorial +layout: blog +--- + +## Performance testing of Flutter apps + +The Flutter framework does a good job being fast by default, but does that mean you don’t need to think about performance at all? No. It is absolutely possible to write Flutter apps that are slow. On the other hand, it is also possible to make the most of the framework, and make your apps not only fast, but also efficient, consuming less CPU time and battery. + +<DashImage figure src="images/1MxtHyEfdBlANwu7ZRyX2mw.webp" alt="This is what we want: a statistically significant result comparing two versions of your app on some meaningful metric. Read on to learn how to get this." caption="This is what we want: a statistically significant result comparing two versions of your app on some meaningful metric. Read on to learn how to get this." /> + + +There are some general guidelines for performance optimization in Flutter: + +* Target as few widgets as possible when updating state. + +* Only update state when you must. + +* Keep computationally intensive tasks outside your build methods, and — ideally — outside the main isolate. + +The hard truth is that, for many questions about performance optimization, the answer is “it depends”. Is *this particular* optimization for *this particular* widget worth the effort and the maintenance cost? Does *this particular* approach in *this particular* situation make sense? + +The only useful answer to these questions comes from testing and measurement. Quantify what impact each choice has on performance, and decide based on that data. + +The good news is that Flutter provides great performance profiling tools, such as the [Dart DevTools](https://flutter.github.io/devtools/) (currently in preview release) that includes the Flutter Inspector, or you can use [Flutter Inspector](https://flutter.github.io/devtools/inspector) directly from Android Studio (with the Flutter plugin installed). You have Flutter Driver for exercising your app, and Profile mode for saving performance information. + +The bad news is that modern smartphones are — well — smart. + +## The problem with governors + +Quantification of Flutter app performance is made particularly hard by iOS and Android governors. These system-level daemons tweak the speed of CPU and GPU units according to load. This is mostly a good thing, of course, because it ensures smooth performance while consuming as little battery as possible. + +The downside is that you can make your app significantly *faster* by making it work *more*. + +Below, you can see how adding a loop with nonsensical print statements to an app made the governor switch into higher gear, making the app much faster and its performance more predictable. + +<DashImage figure src="images/0vJSgoS61dEqAK7kf.webp" alt="The problem with governors: by default, is that you can’t trust your numbers. In this boxplot, we have individual runs on the x axis (labeled by the exact time they started), and build times on the y axis. As you can see, when we introduce some completely unnecessary print statements, it makes build times go down, not up." caption="The problem with governors: by default, is that you can’t trust your numbers. In this boxplot, we have individual runs on the x axis (labeled by the exact time they started), and build times on the y axis. As you can see, when we introduce some completely unnecessary print statements, it makes build times go down, not up." /> + + +In this experiment, worse code resulted in faster build times (above), faster rasterization times, and higher frame rate. When objectively worse code results in better performance metrics, you can’t depend on those metrics for guidance. + +This is just one example of how performance benchmarking of mobile apps can be unintuitive and hard. + +Below, I share some tips I gathered while working on Flutter’s Google I/O app, [Developer Quest](https://github.com/2d-inc/developer_quest). + +## Basic advice + +* Do not measure performance when in DEBUG mode. Only measure performance in profile mode. + +* Measure on a real device, not in the iOS Simulator or Android Emulator. The software emulators are great for development, but they have very different performance characteristics from the real thing. Flutter won’t allow you to run in profile mode on a simulated device, because it doesn’t make any sense. The data you gather this way is not applicable to real world performance. + +* Ideally, use the same exact physical device. Make it your dedicated perf testing device and never use it for anything else. + +* Learn the Flutter [performance profiling tools](https://flutter.dev/docs/testing/ui-performance). + +## CPU/GPU governors + +As discussed above, modern operating systems change the frequency of each CPU and GPU at their disposal according to load and some other heuristics. (For example, touching the screen typically puts an Android phone into a much higher gear.) + +On Android, you can turn these governors off. We call this process “scale locking.” + +* Build a script that scale-locks your performance-test device. You can use [Skia’s recipe](https://github.com/google/skia/blob/e25b4472cdd9f09cd393c9c34651218507c9847b/infra/bots/recipe_modules/flavor/android.py) for inspiration. You can also check out [Unix CPU API](https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-devices-system-cpu). + +* You probably want something less generic and more lightweight unless you’re running a large benchmarking operation like Skia. Have a look at Developer Quest’s [shell script](https://github.com/2d-inc/developer_quest/blob/master/tool/lock_android_scaling.sh) for some pointers. For example, the following excerpt sets a CPU to the `userspace` governor (the only governor that doesn’t change CPU frequencies on its own). + +``` +#!/usr/bin/env bash + +GOV="userspace" +echo "Setting CPU governor to: ${GOV}" +adb shell "echo ${GOV} > /sys/devices/system/cpu/cpu${CPU_NO}/cpufreq/scaling_governor" +ACTUAL_GOV=`adb shell "cat /sys/devices/system/cpu/cpu${CPU_NO}/cpufreq/scaling_governor"` +echo "- result: ${ACTUAL_GOV}" +``` + + +* Your goal here is not to simulate real-world performance (no user scale-locks their device), but to have comparable performance metrics between runs. + +* In the end, you need to experiment, and tailor the shell script to the device you’ll be using. It’s work, but until you do this, your performance data will lie to you. + +<DashImage figure src="images/0PVigpdSw-WlNfJHI.webp" alt="An early version of Developer Quest being exercised by Flutter Driver on my desk." caption="An early version of Developer Quest being exercised by Flutter Driver on my desk." /> + + +## Flutter Driver + +Flutter Driver lets you exercise your app automatically. Read the [Performance profiling](https://flutter.dev/docs/cookbook/testing/integration/profiling) section of flutter.dev for a concrete recipe on how to use it when profiling your app. + +* Don’t exercise your app manually when performance testing. Always use Flutter Driver to ensure that you’re comparing apples to apples. + +* Write your Flutter Driver code so that it exercises what you really want to measure. If you’re after general app performance, try to walk through all parts of your app and do the things a user would. + +* If your app has an element of chance (`Random`, network events, etc.), mock these out. The walk throughs should be as similar to each other as possible. + +* If you want, add custom timeline events by using [Timeline](https://api.dartlang.org/stable/2.2.0/dart-developer/Timeline-class.html)’s `startSync()` and `finishSync()` methods. This is useful, for example, when you are interested in the performance of a particular function. Put `startSync()` at the beginning of it and `finishSync()` at its end. + +* Save both the summary ([writeSummaryToFile](https://docs.flutter.io/flutter/flutter_driver/TimelineSummary/writeSummaryToFile.html)) and, more importantly, the raw timeline ([writeTimelineToFile](https://docs.flutter.io/flutter/flutter_driver/TimelineSummary/writeTimelineToFile.html)). + +* For each version of your app, exercise it many times. For Developer Quest, I converged to 100 runs. (When you measure things that are noisier, like 99th percentile, you might need a lot more runs.) For POSIX-based systems, that just means running something like the following: `for i in {1..100}; do flutter drive --target=test_driver/perf.dart --profile; done`. + +<DashImage figure src="images/1Y7-lpotj3tTjqJyobYyKHw.webp" alt="Using Chrome’s timeline tool to inspect Flutter’s profile output." caption="Using Chrome’s timeline tool to inspect Flutter’s profile output." /> + + +## Timeline + +Timeline is the raw output of your profile run. Flutter dumps this information in a JSON file that can be loaded into `chrome://tracing`. + +* Learn to open the full timeline in Chrome’s tracing timeline. You just open `chrome://tracing` in the Chrome browser, click “Load”, and select the JSON file. You can read more in [this short tutorial](https://aras-p.info/blog/2017/01/23/Chrome-Tracing-as-Profiler-Frontend/). (There is also [Flutter’s timeline tooling](https://flutter.github.io/devtools/timeline), currently in tech preview. I haven’t used that one because the Developer Quest project started before Flutter’s timeline tooling was ready.) + +* Use the WSAD keys to move around the timeline in `chrome://tracing`, and 1234 to change modes of operation. + +* When first setting up your performance testing, consider running Flutter Driver with full Android systrace. This gives you more insight on what’s actually happening in the device, including CPU scaling information. Don’t measure your app with systrace fully on, though, as it makes everything slower and less predictable. + +* How to run full Android systrace with Flutter Driver? First, start Android systrace with `/path/to/your/android/sdk/platform-tools/systrace/systrace.py --atrace-categories=gfx,input,view,webview,wm,am,sm,audio,video,camera,hal,app,res,dalvik,rs,bionic,power,pm,ss,database,network,adb,pdx,sched,irq,freq,idle,disk,load,workq,memreclaim,regulators,binder_driver,binder_lock`. Then, start the app with `flutter run test_driver/perf.dart --profile --trace-systrace`. Lastly, start the Flutter Driver with `flutter drive --driver=test_driver/perf_test.dart --use-existing-app=http://127.0.0.1:NNNNN/` (where NNNNN is the port that `flutter run` above gives you). + +## Metrics + +It’s better to look at as many metrics as possible, but I’ve found some more useful than others. + +* Build times and rasterization times (the metrics that are provided by default by `TimelineSummary`) are only useful for really tight performance tests that don’t include much besides building UI. + +* Don’t look at `TimelineSummary.frameCount` as a way to compute frames per second (FPS). Flutter’s profile tooling does not give you real frame rate information. `TimelineSummary` provides the `countFrames()` method, but it only counts the number of completed frame builds. A well optimized app that limits unnecessary rebuilds will have less frames per second than an unoptimized app that rebuilds often. + +* I personally get the most useful data by measuring the total CPU time spent running Dart code. This counts code executed both in your build methods and outside of them. Assuming you run your profile tests on a scale-locked device, total CPU time is a good approximation of how much more/less battery your app will consume. + +<DashImage figure src="images/00S0itDkQHzRjcY1N.webp" /> + + +* The easiest way to find out the total CPU time spent running Dart code is to measure the extent of `MessageLoop:FlushTasks` events in the timeline. For Developer Quest, I have written [a Dart tool](https://github.com/2d-inc/developer_quest/blob/master/test_driver/parse_timeline.dart#L82) to extract these. + +* To detect jank (i.e. skipped frames), look for extremes. For example, for the particular case of Developer Quest and the device we have for testing, it was helpful to look at 95th percentile build times. (The 90th percentile build times were too similar even when comparing code with vastly different levels of efficiency, and 99th percentile numbers tend to be noisy. Your mileage may vary.) + +<DashImage figure src="images/0ewPiWp1FMME8HDnV.webp" /> + + +* As mentioned above, exercise each version of your app several (perhaps 100) times. Then use average or percentile data with margins of error. Even better, use boxplots. + +## Results + +Once this is set up, you will be able to compare commits and experiments with confidence. Below, you can see an answer to a common dilemma: “is this optimization worth the maintenance overhead?” + +<DashImage figure src="images/1ynM53tboJVQFcpmxRuuu4A.webp" /> + + +I think that in *this particular* case, the answer is yes. With only a few more lines of code, each automated walkthrough of our app takes, on average, 12% less CPU time. + +But—and that is the main message of this article—another optimization’s measurements might show something very different. It is tempting, but wrong, to try to extrapolate a performance measurement too broadly. + +In other words: “it depends”. And we should embrace that sentence. \ No newline at end of file diff --git a/sites/www/content/blog/performance-testing-on-the-web/images/0-d-mmdUUEW08ytWJ.webp b/sites/www/content/blog/performance-testing-on-the-web/images/0-d-mmdUUEW08ytWJ.webp new file mode 100644 index 0000000000..3b8e7de128 Binary files /dev/null and b/sites/www/content/blog/performance-testing-on-the-web/images/0-d-mmdUUEW08ytWJ.webp differ diff --git a/sites/www/content/blog/performance-testing-on-the-web/images/03aZa4m2GbsqHECNw.webp b/sites/www/content/blog/performance-testing-on-the-web/images/03aZa4m2GbsqHECNw.webp new file mode 100644 index 0000000000..dbba11f61c Binary files /dev/null and b/sites/www/content/blog/performance-testing-on-the-web/images/03aZa4m2GbsqHECNw.webp differ diff --git a/sites/www/content/blog/performance-testing-on-the-web/images/0LitJY47Vl0J17C5V.webp b/sites/www/content/blog/performance-testing-on-the-web/images/0LitJY47Vl0J17C5V.webp new file mode 100644 index 0000000000..da3a4acb5f Binary files /dev/null and b/sites/www/content/blog/performance-testing-on-the-web/images/0LitJY47Vl0J17C5V.webp differ diff --git a/sites/www/content/blog/performance-testing-on-the-web/images/0adwmyiS509VwCCYl.webp b/sites/www/content/blog/performance-testing-on-the-web/images/0adwmyiS509VwCCYl.webp new file mode 100644 index 0000000000..51d9345d39 Binary files /dev/null and b/sites/www/content/blog/performance-testing-on-the-web/images/0adwmyiS509VwCCYl.webp differ diff --git a/sites/www/content/blog/performance-testing-on-the-web/images/0imb9-c_LS4XF0xDX.webp b/sites/www/content/blog/performance-testing-on-the-web/images/0imb9-c_LS4XF0xDX.webp new file mode 100644 index 0000000000..d61d2928e1 Binary files /dev/null and b/sites/www/content/blog/performance-testing-on-the-web/images/0imb9-c_LS4XF0xDX.webp differ diff --git a/sites/www/content/blog/performance-testing-on-the-web/images/0tsvQSKBjpWJWkiKM.webp b/sites/www/content/blog/performance-testing-on-the-web/images/0tsvQSKBjpWJWkiKM.webp new file mode 100644 index 0000000000..6751c374fc Binary files /dev/null and b/sites/www/content/blog/performance-testing-on-the-web/images/0tsvQSKBjpWJWkiKM.webp differ diff --git a/sites/www/content/blog/performance-testing-on-the-web/index.md b/sites/www/content/blog/performance-testing-on-the-web/index.md new file mode 100644 index 0000000000..42dac846d5 --- /dev/null +++ b/sites/www/content/blog/performance-testing-on-the-web/index.md @@ -0,0 +1,366 @@ +--- +title: "Performance testing on the web" +description: "Overview" +publishDate: 2020-10-02 +author: tianguang +image: images/0LitJY47Vl0J17C5V.webp +category: tutorial +layout: blog +--- + +*This article was updated on Nov 25, 2020 to use the* `web_benchmarks` *package.* + +## Overview + +During development, we often want to test an app’s performance in the browser. Performance testing is useful, as it reveals potential bugs that make an app slower. + +This article describes a way to test an app’s performance in Chrome. This method is similar to how we test the [new Flutter Gallery](https://gallery.flutter.dev/#/)’s performance. + +## Example app + +We use a simple app that contains an appbar, a floating action button, and an infinite list of items. The list also shows the number of times the button is pushed. + +<DashImage figure src="images/0adwmyiS509VwCCYl.webp" /> + + +The app has a second page containing some information. + +<DashImage figure src="images/0-d-mmdUUEW08ytWJ.webp" /> + + +You can clone the app here: + +* [https://github.com/material-components/material-components-flutter-experimental/tree/develop/web_benchmarks_example](https://github.com/material-components/material-components-flutter-experimental/tree/develop/web_benchmarks_example) + +## What to test? + +We want to test the app’s performance in Chrome under the following usage scenarios: + +1. User scrolls through the infinite list. + +1. User switches between the two pages. + +1. User taps the floating action button. + +## Setting up the framework + +Add the following to `pubspec.yaml`: + +```yaml +dependencies: + flutter: + sdk: flutter + web_benchmarks: ^0.0.2 + +``` + +This dependency pulls in [`web_benchmarks`](https://pub.dev/packages/web_benchmarks), a minimal package that implements performance testing in Chrome. + +This package is adapted from [`macrobenchmarks`](https://github.com/flutter/flutter/tree/master/dev/benchmarks/macrobenchmarks) and [`devicelab`](https://github.com/flutter/flutter/tree/master/dev/devicelab), two packages used by Flutter for web performance testing on the Flutter Gallery. At the moment, these two packages are specialized for web performance testing within `flutter/flutter`, so it is easier to import the more general package, [`web_benchmarks`](https://pub.dev/packages/web_benchmarks). + +Run `flutter pub get` to pull in this package. + +## Writing the first test + +Add a `benchmarks` directory under `lib`, and add a new dart file to it called `runner.dart`: + +<DashImage figure src="images/0tsvQSKBjpWJWkiKM.webp" /> + + +The contents of the file are as follows: + +```dart +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:web_benchmarks/client.dart'; +import 'package:web_benchmarks_example/main.dart'; +import 'package:web_benchmarks_example/homepage.dart' show textKey; + +/// A recorder that measures frame building durations. +abstract class AppRecorder extends WidgetRecorder { + AppRecorder({@required this.benchmarkName}) : super(name: benchmarkName); + + final String benchmarkName; + + Future<void> automate(); + + @override + Widget createWidget() { + Future.delayed(Duration(milliseconds: 400), automate); + return MyApp(); + } + + Future<void> animationStops() async { + while (WidgetsBinding.instance.hasScheduledFrame) { + await Future<void>.delayed(Duration(milliseconds: 200)); + } + } +} + +class ScrollRecorder extends AppRecorder { + ScrollRecorder() : super(benchmarkName: 'scroll'); + + Future<void> automate() async { + final scrollable = Scrollable.of(find.byKey(textKey).evaluate().single); + await scrollable.position.animateTo( + 30000, + curve: Curves.linear, + duration: Duration(seconds: 20), + ); + } +} + +Future<void> main() async { + await runBenchmarks( + { + 'scroll': () => ScrollRecorder(), + }, + ); +} + +``` + +### What is this test doing? + +* When this app runs, a `ScrollRecorder` object is created, which drives the app by automatically making gestures. In this case, shortly after the app starts, it starts scrolling down the infinite list. + +* The `ScrollRecorder` class extends the `AppRecorder` class, which extends the `WidgetRecorder` class, which also records performance data as it drives the app. + +* `runBenchmarks` is a function defined in `package:web_benchmarks/client.dart`, which allows the user to select which benchmark to run, and displays the results in the browser. + +* The method `automate` uses the `flutter_test` package, which provides methods to make gestures or find certain widgets in an app. + +## Running the first test + +In the root directory of the project, run `flutter run -d chrome -t lib/benchmarks/runner.dart`. This tells Flutter to use `runner.dart` as the entry point, instead of `main.dart`. + +<DashImage figure src="images/0imb9-c_LS4XF0xDX.webp" /> + + +We only have one benchmark so far, so click “scroll” to start it. + +<DashImage figure src="images/03aZa4m2GbsqHECNw.webp" /> + + +The test begins, and the list automatically scrolls down. + +The test ends in a few seconds, showing the following screen: + +<DashImage figure src="images/0LitJY47Vl0J17C5V.webp" /> + + +This chart shows the time it took for the app to draw each (recorded) frame. The horizontal axis represents the flow of time; the vertical axis, the duration each frame took. + +The first 2/3 of the chart has a gray background; these frames are considered “warm-up frames”, and are omitted from the statistics. Warm-up frames typically give the JIT compiler time to compile the code, and populate various caches, so that the measured frames produce numbers that reflect the “eventual” performance of the app, rather than the first few seconds of it. The warm-up phase should not be always ignored — it can provide valuable information about your app’s performance during the first few seconds, which can still influence the perception of the app’s quality. + +Red frames are “outliers” — they are frames which take significantly longer than other frames to draw. Some outliers can be nearly unnoticeable. For example, jank at the beginning or the end of an animation up to a certain point will not be visible. However, a janky frame in the *middle* of an animation will be very noticeable. + +Outliers provide a good indicator of the jankiness of the app. By improving your app, you can lower the values of outliers or reduce the number of outliers, which shows that your app has become smoother. + +## Collecting data from Chrome’s DevTools + +This benchmark is entirely run from inside Chrome. Add the following file as `test/run_benchmarks.dart`: + +```dart +import 'dart:convert' show JsonEncoder; +import 'dart:io'; + +import 'package:web_benchmarks/server.dart'; + +Future<void> main () async { + final taskResult = await serveWebBenchmark( + benchmarkAppDirectory: Directory('.'), + entryPoint: 'lib/benchmarks/runner.dart', + useCanvasKit: false, + ); + print (JsonEncoder.withIndent(' ').convert(taskResult.toJson())); +} + +``` + +Then, run `dart test/run_benchmarks.dart`. + +After about one minute, you should see the following results: + +```text +Received profile data +{ + "scroll": [ + { + "metric": "preroll_frame.average", + "value": 122.22105263157894 + }, + { + "metric": "preroll_frame.outlierAverage", + "value": 767.4 + }, + { + "metric": "preroll_frame.outlierRatio", + "value": 6.278787356816812 + }, + { + "metric": "preroll_frame.noise", + "value": 0.35245125929858917 + }, + { + "metric": "apply_frame.average", + "value": 529.8888888888889 + }, + { + "metric": "apply_frame.outlierAverage", + "value": 2696.5 + }, + { + "metric": "apply_frame.outlierRatio", + "value": 5.0888026840008385 + }, + { + "metric": "apply_frame.noise", + "value": 0.4154574507186871 + }, + { + "metric": "drawFrameDuration.average", + "value": 2562.0243902439024 + }, + { + "metric": "drawFrameDuration.outlierAverage", + "value": 6828.888888888889 + }, + { + "metric": "drawFrameDuration.outlierRatio", + "value": 2.66542696271474 + }, + { + "metric": "drawFrameDuration.noise", + "value": 0.4512335390240227 + }, + { + "metric": "totalUiFrame.average", + "value": 4971 + } + ] +} + +``` + +The exact benchmark values may vary depending on the machine. + +### What is this test doing? + +* Running `test/run_benchmarks.dart` builds the app for the web. Then, it starts a Chrome instance and runs the app in it. + +* `test/run_benchmarks.dart` connects to Chrome’s DevTools port, and listens and collects relevant performance data from it. + +### What do the results mean? + +* When rendering a frame, the [layer tree](https://api.flutter.dev/flutter/rendering/Layer-class.html) is walked twice. + +* “Preroll” is the first walk. It does not render anything, but it computes values that are later used for rendering. Examples include: transform matrices, the inverse of transforms, and clips. + +* “Apply frame” is the second walk where the UI is actually rendered. + +* “Draw frame” is the total time that the framework takes to render a frame. It includes “Preroll” and “Apply frame”, but it also includes the time spent on building and laying out the widgets. + +* “Total UI frame” includes everything in “Draw frame”, but it also includes some hidden work that the browser performs, such as layer tree updates, style recalculations, and browser-side layout (not to be confused with Flutter’s own layout). + +* When a dataset (a list of durations) is collected, the algorithm removes outliers. + +* First, the mean and standard deviation of the data are computed, and any data point that is higher than (mean + 1 standard deviation) is considered an outlier. + +* The mean and standard deviation of non-outliers (clean data) are used to compute the average and noise of the data set, which are then reported. + +* The mean of all outliers, as well as the ratio of the “outlier mean” and the “non-outlier mean” are also reported. + +* For each dataset, “outlierRatio” and “noise” are both good indicators of how much noise there is in the performance of the app. If the results are too noisy, it might indicate inconsistencies in performance (such as janky frames as GC pauses). By aiming to lower the noise, you can make your app perform more smoothly. + +## Add more tests + +Edit `lib/benchmarks/runner.dart` to add two more tests. + +First, modify the `main` function: + +```dart +Future<void> main() async { + await runBenchmarks( + { + 'scroll': () => ScrollRecorder(), + 'page': () => PageRecorder(), + 'tap': () => TapRecorder(), + }, + ); +} + +``` + +Finally, add two more classes that extend `AppRecorder`: + +```dart +class PageRecorder extends AppRecorder { + PageRecorder() : super(benchmarkName: 'page'); + + bool _completed = false; + + @override + bool shouldContinue() => profile.shouldContinue() || !_completed; + + Future<void> automate() async { + final controller = LiveWidgetController(WidgetsBinding.instance); + for (int i = 0; i < 10; ++i) { + print('Testing round $i...'); + await controller.tap(find.byKey(aboutPageKey)); + await animationStops(); + await controller.tap(find.byKey(backKey)); + await animationStops(); + } + _completed = true; + } +} + +class TapRecorder extends AppRecorder { + TapRecorder() : super(benchmarkName: 'tap'); + + bool _completed = false; + + @override + bool shouldContinue() => profile.shouldContinue() || !_completed; + + Future<void> automate() async { + final controller = LiveWidgetController(WidgetsBinding.instance); + for (int i = 0; i < 10; ++i) { + print('Testing round $i...'); + await controller.tap(find.byIcon(Icons.add)); + await animationStops(); + } + _completed = true; + } +} + +``` + +### What are these tests doing? + +* We have added the two remaining benchmark tests: one for switching between pages, and the other for tapping on the floating action button. + +* `animationStops` repeatedly checks whether an animation is happening, and stops when all animation has stopped. This ensures, for example, a successful transition to the “about” page. + +* In the “page” and “tap” benchmarks, the `_completed` boolean tracks whether the automated gestures have finished. + +* In the “page” and “tap” benchmarks, overriding the `shouldContinue` method causes the `AppRecorder` to stop recording frames **after** all gestures have finished. + +### How to run these tests? + +To run these tests (and see the animations) in Chrome, run: + +* `flutter run -d chrome -t lib/benchmarks/runner.dart --profile` + +To run these tests and collect DevTools data, run: + +* `dart test/run_benchmarks.dart` + +## What next? + +Once you have a way to collect performance data, you can use it however you want: + +* You can set up a job in CI that runs these benchmark tests whenever someone submits a PR, to avoid introducing performance-heavy changes. + +* You can also set up a dashboard that keeps track of the trend of performance benchmarks. This is what we are doing for the Flutter Gallery (see [Flutter Dashboard](https://flutter-dashboard.appspot.com/#/build)). \ No newline at end of file diff --git a/sites/www/content/blog/perspective-on-flutter/images/1WdpLQEC_L1PYDO1KgxxfMQ.webp b/sites/www/content/blog/perspective-on-flutter/images/1WdpLQEC_L1PYDO1KgxxfMQ.webp new file mode 100644 index 0000000000..b8a10ac6a8 Binary files /dev/null and b/sites/www/content/blog/perspective-on-flutter/images/1WdpLQEC_L1PYDO1KgxxfMQ.webp differ diff --git a/sites/www/content/blog/perspective-on-flutter/images/1_hmzjJSeyWTnPccV5BsOHg.webp b/sites/www/content/blog/perspective-on-flutter/images/1_hmzjJSeyWTnPccV5BsOHg.webp new file mode 100644 index 0000000000..41910ce8ee Binary files /dev/null and b/sites/www/content/blog/perspective-on-flutter/images/1_hmzjJSeyWTnPccV5BsOHg.webp differ diff --git a/sites/www/content/blog/perspective-on-flutter/images/1cWO2jnAleFiGh8J-vvpNrQ.gif b/sites/www/content/blog/perspective-on-flutter/images/1cWO2jnAleFiGh8J-vvpNrQ.gif new file mode 100644 index 0000000000..55ad1ccaca Binary files /dev/null and b/sites/www/content/blog/perspective-on-flutter/images/1cWO2jnAleFiGh8J-vvpNrQ.gif differ diff --git a/sites/www/content/blog/perspective-on-flutter/images/1sSCRNrjBJYifAMUMpIbL7A.webp b/sites/www/content/blog/perspective-on-flutter/images/1sSCRNrjBJYifAMUMpIbL7A.webp new file mode 100644 index 0000000000..458c2cacd0 Binary files /dev/null and b/sites/www/content/blog/perspective-on-flutter/images/1sSCRNrjBJYifAMUMpIbL7A.webp differ diff --git a/sites/www/content/blog/perspective-on-flutter/index.md b/sites/www/content/blog/perspective-on-flutter/index.md new file mode 100644 index 0000000000..e0c82a5901 --- /dev/null +++ b/sites/www/content/blog/perspective-on-flutter/index.md @@ -0,0 +1,266 @@ +--- +title: "Perspective on Flutter" +description: "Fun with 3D and the Transform widget" +publishDate: 2018-06-18 +author: wmleler1 +image: images/1WdpLQEC_L1PYDO1KgxxfMQ.webp +category: spotlight +layout: blog +--- + +tl;dr — The Transform widget allows you to do amazing things in your Flutter apps. Here’s a one-minute video showing off what developers have done with Transform widgets: + +<YoutubeEmbed id="9z_YNlRlWfA" title="Transform (Flutter Widget of the Week)" fullwidth="true"/> + + +This article builds a simple demo program that shows how to use Flutter’s [`Transform`](https://docs.flutter.io/flutter/widgets/Transform-class.html) widget to provide 3D perspective, a *graphic* demonstration of the ease with which Flutter can do something that would be rather difficult to implement in a system based on native widgets. It is also fun to play with! + +Here’s the app we are going to build in action (the little circle reflects the position of the user’s finger on the screen): + +<DashImage figure src="images/1WdpLQEC_L1PYDO1KgxxfMQ.webp" /> + + +## Getting started + +This example starts with the familiar default Flutter app — what you get when you do a `flutter create` or use your IDE to generate a new Flutter project. We are going to add two new things to this app: a [`Transform`](https://docs.flutter.io/flutter/widgets/Transform-class.html) widget and a [`GestureDetector`](https://docs.flutter.io/flutter/widgets/GestureDetector-class.html) widget. + +First the transform: + +```dart +// v1: move default app to separate function with fixed name +// Add transform widget, rotate and perspective +import 'package:flutter/material.dart'; + +void main() => runApp(MyApp()); + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Perspective', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: MyHomePage(), + ); + } +} + +class MyHomePage extends StatefulWidget { + MyHomePage({Key key}) : super(key: key); // changed + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State<MyHomePage> { + int _counter = 0; + Offset _offset = Offset(0.4, 0.7); // new + + void _incrementCounter() { + setState(() { + _counter++; + }); + } + + @override + Widget build(BuildContext context) { + return Transform( // Transform widget + transform: Matrix4.identity() + ..setEntry(3, 2, 0.001) // perspective + ..rotateX(_offset.dy) + ..rotateY(_offset.dx), + alignment: FractionalOffset.center, + child: _defaultApp(context), + ); + } + + _defaultApp(BuildContext context) { // new + return Scaffold( + appBar: AppBar( + title: Text('The Matrix 3D'), // changed + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'You have pushed the button this many times:', + ), + Text( + '$_counter', + style: Theme.of(context).textTheme.display1, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: Icon(Icons.add), + ), + ); + } + +} + +``` + +Running this code shows the default app being transformed slightly in 3D, with perspective: + +<DashImage figure src="images/1_hmzjJSeyWTnPccV5BsOHg.webp" /> + + +We also removed some things we didn’t need, including all the extra comments, and (in honor of [Dart 2](https://www.dartlang.org/dart-2)) all the `new` keywords. For demonstration purposes we moved the layout part of the default app (the `build` method of `_MyHomePageState)` to a separate method called `_defaultApp` (lines 49–74). And for simplicity we set the AppBar title on line 52, rather than passing it as a parameter to `MyHomePage.` + +## Transform widget + +The added [Transform](https://docs.flutter.io/flutter/widgets/Transform-class.html) widget is on lines 39–46. Let’s look at this more closely. Transform takes a 3D [transformation matrix](https://en.wikipedia.org/wiki/Transformation_matrix), which is a [Matrix4](https://docs.flutter.io/flutter/vector_math/Matrix4-class.html). Why a 3D matrix? Isn’t Flutter for two-dimensional graphics? Well, kind of. + +Pretty much all but the least powerful smartphones include amazingly fast [GPUs](https://en.wikipedia.org/wiki/Graphics_processing_unit), which are optimized for 3D graphics. That means that rendering 3D graphics is *very fast*. Consequently, almost everything you see on your phone is being rendered in 3D, even the 2D stuff. Crazy, huh? + +Setting the transformation matrix lets us manipulate what is being viewed (in 3D even!). Common transformations include translate, rotate, scale, and perspective. To create this matrix we start with an [identity matrix](https://docs.flutter.io/flutter/vector_math/Matrix4/Matrix4.identity.html) (line 40) and then apply transformations to it. Transformations are not [commutative](https://en.wikipedia.org/wiki/Commutative_property), so we have to apply them in the right order. The final complete matrix will be sent to the GPU to transform the objects being rendered. + +Transformations are a complicated subject, but if you want to learn more about them you can read any introduction to 3D graphics on [transformation matrices](https://en.wikipedia.org/wiki/Transformation_matrix) and [homogeneous coordinates](https://en.wikipedia.org/wiki/Homogeneous_coordinates). + +## Perspective + +The first transformation (on line 41) implements perspective. [Perspective](https://en.wikipedia.org/wiki/Perspective_(graphical)) makes objects that are farther away appear smaller. Setting row 3, column 2 of the matrix to 0.001 scales things down based on their distance. + +Where did the number 0.001 come from? Thin air! You can play with this number to increase and decrease the amount of perspective, something like zooming in and out with a zoom lens on a camera. The bigger this number, the more pronounced is the perspective, which makes it look like you are closer to the viewed object. + +Flutter does provide a [`makePerspectiveMatrix`](https://docs.flutter.io/flutter/vector_math/makePerspectiveMatrix.html) function but that method includes arguments for setting the aspect ratio, field of view, and near and far planes — way more than we need — so we will just set the required element of the matrix directly. + +## Rotations + +Next on lines 42 and 43 we apply two rotations based on the value of the `_offset` variable (from line 29; later we will use this variable to track the position of the user’s finger). Curiously, the X rotation is based on the Y offset, and the Y rotation is based on the X offset. Why? + +<DashImage figure src="images/1sSCRNrjBJYifAMUMpIbL7A.webp" /> + + +Consider this image, which has added green arrows showing the X and Y axes for the display. The default origin of these axes is the upper left corner of the display (which is why the Y axis points down), but our program sets the origin (in line 44) to be the center of the display . + +Rotation is defined about an axis, so `rotateX` defines rotation around the X axis, which tilts in the Y (up-down) direction. Likewise, `rotateY` tilts in the X (left-right) direction (around the Y axis). That’s why `rotateX` is controlled by `_offset.dy` and `rotateY` is controlled by `_offset.dx`. + +There is also a Z axis, whose origin is at the surface of the screen running perpendicular to the screen through the phone and out the back, so that the Z value goes positive the further away a thing is from the viewer. Consequently, the `rotateZ` method rotates in the plane of the display. + +## Interaction + +The second and final thing we are going to add is a [GestureDetector](https://docs.flutter.io/flutter/widgets/GestureDetector-class.html) widget. This is very easy in Flutter. + +```dart +// v2: add Gesture detector +import 'package:flutter/material.dart'; + +void main() => runApp(MyApp()); + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Perspective', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: MyHomePage(), + ); + } +} + +class MyHomePage extends StatefulWidget { + MyHomePage({Key key}) : super(key: key); // changed + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State<MyHomePage> { + int _counter = 0; + Offset _offset = Offset.zero; // changed + + void _incrementCounter() { + setState(() { + _counter++; + }); + } + + @override + Widget build(BuildContext context) { + return Transform( // Transform widget + transform: Matrix4.identity() + ..setEntry(3, 2, 0.001) // perspective + ..rotateX(0.01 * _offset.dy) // changed + ..rotateY(-0.01 * _offset.dx), // changed + alignment: FractionalOffset.center, + child: GestureDetector( // new + onPanUpdate: (details) => setState(() => _offset += details.delta), + onDoubleTap: () => setState(() => _offset = Offset.zero), + child: _defaultApp(context), + ) + ); + } + + _defaultApp(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('The Matrix 3D'), // changed + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'You have pushed the button this many times:', + ), + Text( + '$_counter', + style: Theme.of(context).textTheme.display1, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: Icon(Icons.add), + ), + ); + } + +} + +``` + +In line 28, `_offset` is initialized to zero. Lines 44–48 define a GestureDetector that detects two kinds of gestures: pan gestures (e.g., a finger moving around the screen) and double tap gestures. Line 45 adds the amount of pan movement (in pixels) to `_offset`. Line 46 resets the `_offset` to zero when the user double taps the display. For both of these gestures, `setState()` schedules the display to be redrawn. + +Finally, lines 41 and 42 are modified so that the offset (in pixels) is scaled by a factor of 0.01 to make it better to use for rotation (which is in radians, where a complete rotation is 2π — approximately 6.28 — so a complete rotation requires a pan movement of 628 pixels). You can play with the scale factor to make the rotation more or less sensitive to finger movement. Also, the parameter to `rotateY` is negated because as the finger moves to the right, the image rotates counter-clockwise around the Y axis (because the Y axis is pointing downward). + +## Done! + +[In Flutter](https://hackernoon.com/whats-revolutionary-about-flutter-946915b09514), almost everything is in the app itself (rather than in the platform), including the widgets and renderer. This gives great flexibility. In this case, we had easy access to powerful features provided by the GPU, and even a widget to help us. Our changes consist of a mere 13 lines of code. Not bad! + +Note that after you rotate the default app around the X or Y axis, you might have difficulty tapping the FAB to increment the counter. Flutter does compensate for most transforms, including scale and rotateZ, so the UI will still work properly in those cases, but it has some problems with full 3D transforms. We’re working on fixing that. + +Of course, none of the objects in the default app are actually 3D. They are all 2D (flat) objects (even the faux shadows cast by the AppBar and FAB). But that doesn’t stop us from spinning them around in 3D space. + +## Further ideas + +There is so much you could do with this simple program, often with just a few lines of code: + +* Make the same changes to some app that is more complex than the default app. + +* Add a spin around the Z axis, using `rotateZ`. + +* Change the amount of perspective so that it is a negative number. What do you think will happen? + +* Translate the [FAB](https://docs.flutter.io/flutter/material/FloatingActionButton-class.html) a bit in the negative Z direction so that it is floating slightly above the body of the [Scaffold](https://docs.flutter.io/flutter/material/Scaffold-class.html) widget. For extra points, create a “real” shadow instead of a faux one. + +* Add an animation so that when the user’s finger releases from the screen the app keeps spinning, slowing down to a stop. You can do this with the `onPanEnd` argument to the GestureDetector, which provides the [velocity of the finger](https://docs.flutter.io/flutter/gestures/DragEndDetails-class.html) (in pixels per second) as it loses contact with the screen. + +* Create a 3D transition between two screens or tabs in an app. For example, like turning the pages of a book, or one screen spinning and becoming another. + +If you think of other ideas, let me know in the comments. + +UPDATE: A shout out to the article “[Make 3D flip animation in Flutter](https://medium.com/flutter-community/make-3d-flip-animation-in-flutter-16c006bb3798)”, which references this article to add perspective to a flip animation, like this: + +<DashImage figure src="images/1cWO2jnAleFiGh8J-vvpNrQ.gif" /> diff --git a/sites/www/content/blog/perspectives-from-early-adopters-of-flutter-as-a-game-development-tool/images/0ZrpkIfhAQ0MW1RYp.webp b/sites/www/content/blog/perspectives-from-early-adopters-of-flutter-as-a-game-development-tool/images/0ZrpkIfhAQ0MW1RYp.webp new file mode 100644 index 0000000000..33f0077a8c Binary files /dev/null and b/sites/www/content/blog/perspectives-from-early-adopters-of-flutter-as-a-game-development-tool/images/0ZrpkIfhAQ0MW1RYp.webp differ diff --git a/sites/www/content/blog/perspectives-from-early-adopters-of-flutter-as-a-game-development-tool/images/0n0cpV0PkueLSiSYx.webp b/sites/www/content/blog/perspectives-from-early-adopters-of-flutter-as-a-game-development-tool/images/0n0cpV0PkueLSiSYx.webp new file mode 100644 index 0000000000..e08bd192e1 Binary files /dev/null and b/sites/www/content/blog/perspectives-from-early-adopters-of-flutter-as-a-game-development-tool/images/0n0cpV0PkueLSiSYx.webp differ diff --git a/sites/www/content/blog/perspectives-from-early-adopters-of-flutter-as-a-game-development-tool/images/1gq-r9Mn1BzgKL8XYLnFsEQ.webp b/sites/www/content/blog/perspectives-from-early-adopters-of-flutter-as-a-game-development-tool/images/1gq-r9Mn1BzgKL8XYLnFsEQ.webp new file mode 100644 index 0000000000..f0e38f0bfb Binary files /dev/null and b/sites/www/content/blog/perspectives-from-early-adopters-of-flutter-as-a-game-development-tool/images/1gq-r9Mn1BzgKL8XYLnFsEQ.webp differ diff --git a/sites/www/content/blog/perspectives-from-early-adopters-of-flutter-as-a-game-development-tool/index.md b/sites/www/content/blog/perspectives-from-early-adopters-of-flutter-as-a-game-development-tool/index.md new file mode 100644 index 0000000000..3f6fa9dab8 --- /dev/null +++ b/sites/www/content/blog/perspectives-from-early-adopters-of-flutter-as-a-game-development-tool/index.md @@ -0,0 +1,220 @@ +--- +title: "Perspectives from early adopters of Flutter as a game development tool" +description: "How user research shaped the Flutter Casual Game Toolkit" +publishDate: 2022-05-12 +author: taodong +image: images/0ZrpkIfhAQ0MW1RYp.webp +category: announcements +layout: blog +--- + +## Introduction + +During this week’s Google I/O, we unveiled the [Flutter Casual Game Toolkit](http://flutter.dev/games), a set of resources to support game builders in the Flutter community. Up until that point, Flutter hadn’t focused on game development as an officially supported use case, though we had been impressed by success stories such as the popular word puzzle game [4 Pics 1 Word](https://apps.apple.com/app/4-pics-1-word/id595558452) and community efforts such as the [Flame](https://flame-engine.org/) engine. *So why did we decide it’s time to officially get behind game development, and how did we determine what the initial release of the Toolkit should include?* In this post, we share a couple user studies we conducted with the early adopters of Flutter as a game development tool, and explain how their perspectives shaped our strategy and the design of the Toolkit. + +We started our investigation with an interview study, where we met individually with 6 developers (referred to as P01 to P06) who had successfully built and released Flutter games. We had two objectives for this study. The first was to understand which types of resources can be most helpful for getting started on Flutter game development. The second was to better understand Flutter developers’ motivations, needs, and pain points in game development and leverage that understanding to strategically plan our long-term investment in this area. To complement the qualitative research findings from the interviews, we also ran a survey on the topic in the broad Flutter user community. + +In the rest of the article, we first summarize the main findings from both the interview and survey studies, and then we share recommendations on market positioning, documentation and learning resources, and potential long-term investments to support Flutter game developers. + +## Findings from developer interviews + +### Background of Flutter game developers + +In general, we found two archetypes of Flutter game developers that exist today: + +* **App-game hybrid developers**: They’re Flutter app developers, but they’re also leveraging Flutter to build relatively simple games without using another tool. (P02, P04, and P05) + +* **Computational artists and graphics enthusiasts**: They’re primarily attracted by the high degree of control over graphics offered by Flutter, but they might not be using Flutter for app development. (P01 and P03) + +The following table provides more detail about the background of the developers we interviewed in this study. Several were referred to us by the Flame team. + +```markdown + +ID | Background | Types of games | Experience with game engines +-- | -- | -- | -- +P01 | BFA in hand-drawn animation;  Developed video game graphic pipeline tools at small video game studio; Mobile game developer since 2011. | Puzzle | Unity, Godot, Flame +P02 | 10 years software development experience mostly in web; P03’s company migrated mobile app stack from Xamarin to Flutter; Started own consulting firm in 2018 and a client requested a mobile game in Flutter after reading P02’s blog post about Flame. | Puzzle | Microsoft XNA, Unity, Flame +P03 | Uses C# and C++ at work for 3D modeling. Started developing a new game engine and were into graphic pipelines. P03 started with Flutter in 2019 on a 1-month side game project. | 2D Shooter, Platform game | Unity, Unreal, Godot, Flame +P04 | Has experience in Java, frontend development with Angular. 2 years ago, P04 started mobile development with Kotlin/Android. | Platform game | Flame, some exposure to Unity and Unreal +P05 & P06 | Established a game studio specializing in casual games for over 15 years. P05 & P06 worked on the same game and were interviewed together. | Word puzzle | None + + +``` + +### Perceived benefits of using Flutter to develop games + +Participants named multiple benefits of using Flutter to develop games. We list those mentioned by multiple participants below: + +* **Simplicity**: Participants believed Flutter was easier to get started with and less overwhelming than larger game engines, such as Unity and Unreal, for building 2D casual games. (P03, P04) + +* **Control**: Participants appreciated the ability to see Flutter’s source code from within the IDE, and have full and direct control of the canvas. (P01, P02, P04) + +* **Ecosystem**: Developers can leverage packages in the Flutter ecosystem such as data persistence and audio playback. (P01, P02, P03, P04) Specifically, P01 considered Flutter’s audio capabilities superior to other cross-platform UI toolkits. + +* **Cross-platform:** Participants appreciated the ease of deploying to iOS and Android and thought it delivered “a true single codebase.” (P01) This was, however, not mentioned frequently, since game engines are often cross-platform. + +* **Ease of building UI:** A Flutter game can easily leverage any Flutter widget in its UI. (P04, P05) This is especially useful for games that are more app-like and don’t require using a game engine (for example, a word puzzle game that P05 and P06 built). + +* **Learning opportunities**: Flutter and Flame’s open source nature provides an opportunity to develop a good understanding of game engines and the graphics pipeline through building games. (P03) + +* **The Flame game engine:** Participants who use Flame generally speak positively about the project in both its capabilities and the support that Flame provides. P04 specifically built a game on Flutter because he discovered Flame. + +### Perceived challenges of using Flutter to develop games + +As an emerging use case, developing games on Flutter is not without challenges. Participants highlighted the following issues they experienced in their respective projects: + +* Integration and ecosystem: +– Difficulty integrating with Play game services. (P01, P03) +– Missing good options for offline-first databases with conflict management. (P05) +– Guidance is needed to select the best audio plugin that can play audio in foreground and background simultaneously. (P05) +Assets: finding and creating high-quality game assets are time consuming. (P02, P03, P04) +> *It’s probably the most time-consuming process. If I don’t find the assets that go along with each other, the game doesn’t look exactly how I wanted. And since I’m not that good at making art, finding the right assets is the most challenging task for me. (P03)* + +* Flame-specific challenges: +– Participants noted that ****Flame’s documentation is not beginner friendly. In particular, [Forge2D](https://github.com/flame-engine/forge2d) (part of Flame) could use better documentation. (P03, P04) +– Participants noted that collision detection in Flame is too basic. It doesn’t handle physics after two objects have collided, unless you use Forge2D. (P03) + +* Missing features in Flutter’s SDK and tooling: +– Lacking 3D support. (P02, P03) However, this doesn’t seem critical to these participants. +– No direct shader access for filters, effects, layers, or compositing. (P01, P02) This is somewhat important to participants who’re into computer graphics. +– Lacks user-space APIs to control refresh rate, such as 120hz. (P01, P02) +– Not sure if Flutter has GPU debugging tools that provide visibility into the frame buffer. Xcode has great OpenGL debugging tools. (P01) + +* Performance and bundle size: +– DevTools isn’t always helpful in diagnosing performance issues on a game with a game loop. (P02) +– Animation janks are present on low-end iOS devices. The shader warm-up workaround is useful but labor-intensive. (P05) There’s also no video tutorial on how to do this. (P03) +– Lack of support on adapting app rendering qualities to the performance characteristics of the device. (P05) +– Web bundle size is too large for web gaming platforms, such as Facebook Instant Game. (P05) + +### Comparing Flutter with alternative game development tools + +All participants had limited experiences with other game engines due to the selection criteria of this study. However, they offered their impressions regarding onboarding, sense of control, and package ecosystems. + +First, participants had the impression that Flutter/Flame was easier to get started with and understand than bigger game engines such as Unity and Unreal. (P01, P02, P03, P04) Participants found that many features in Unity or Unreal weren’t related to the kinds of games they were building. + +Second, since our participants were experienced coders, they found it difficult to gain the control they needed from the low-code development environment in Unity or Unreal. +> *I really like getting in and having full nitty-gritty control of things. So, with Flutter, the Flame engine’s pretty wide open. I feel like I have real full control, whereas in Unity, everything’s buried. They try to automate things for casual users. (P01)* + +Last but not least, Flutter’s ecosystem was considered an advantage over other lightweight game engines such as [Godot](https://godotengine.org/) (P02, P03). For example, P03 said with `package:hive`, data persistence on the native platform was easy. He suspected that native coding would probably be necessary with Godot. + +## Useful resource for first-time Flutter game developers + +All participants mentioned needing better documentation and learning resources when starting developing games in Flutter. To prioritize, we asked participants to assess the importance of different kinds of resources for game development by categorizing them into three buckets: *Must-Have*, *Should-Have*, and *Could Have*. The following lists show the resulting buckets: + +The “Must-Have” resources include: + +* **A sample game** with common game-specific techniques (such as audio support) and platform gaming services (such as Google Play services) pre-integrated. + +* **A dedicated website** that includes all game-development resources and recommendations on third-party services (such as packages, asset marketplaces). + +* **How-to video(s)** that covers basic game development concepts (such as the game loop), integration with backend, game support, and monetization services. P03 was also interested in long-form videos such as [the boring show](https://www.youtube.com/playlist?list=PLjxrf2q8roU3ahJVrSgAnPjzkpGmL9Czl) to go through the whole process of game development including troubleshooting: +> *I feel that two different kinds of [video] content will be better. One would be something very short, similar to Widget of the Week, and then something similar to the Boring Flutter Show where you kind of go into the whole process and go through all the steps of creating that game. (P03)* + +The “Should-Have” resources include: + +* A dedicated Flutter for Games discord channel that is more general than the Flame team’s Discord. + +The “Could-Have” resources include: + +* Codelabs + +* Instructor-led workshops + +* Credits for Google services (such as GCP, Ads, Firebase) + +* Direct access to dedicated support + +Based on the valuable input from participants, we prioritized the “Must-Have” resources in the initial release of the Casual Game Toolkit. + +## Findings from the survey + +As mentioned, we conducted a complementary survey to get a broader view on game development in the Flutter community. The following figure shows the high-level results: + +### Flutter users are interested in using Flutter for game development + +It’s unsurprising that many Flutter users (62.4%) didn’t know that they could use Flutter to write games. However, 39.2% were interested in learning more. + +<DashImage figure src="images/0n0cpV0PkueLSiSYx.webp" /> + + +The wide interest in building games with Flutter is also reflected in the fact that many Flutter users are no strangers to game development in general. Out of 936 respondents, 500+ had experience with Unity, 130+ with Unreal, 120+ with [Flame](https://docs.flame-engine.org/1.0.0/#), and 80+ with Godot. It’s encouraging to see that many developers are already equipped with game development skills. + +### Many Flutter game developers are just getting started + +We then asked those who were either aware of or interested in Flutter’s ability to build games to tell us where they’re at in their game development process. Within this group, 43.2% hadn’t started a concrete game project yet, suggesting the need for getting-started support. + +<DashImage figure src="images/0ZrpkIfhAQ0MW1RYp.webp" /> + + +### There are more Flutter games out there than we thought + +We asked respondents to point us to any games they have built with Flutter. To our pleasant surprise, we received 148 responses. Many of those games were 2D puzzles or [platform games](https://www.google.com/search?q=platformers+definition&oq=platform&aqs=chrome.0.69i59j46i67i433j69i64l3j69i65j69i60j69i61.2458j0j7&sourceid=chrome&ie=UTF-8), while a handful of them had attempted pseudo-3D effects using Flutter. + +It seems to be entirely possible to build highly successful mobile games with Flutter. For example, [Kelimelik](https://play.google.com/store/apps/details?id=com.he2apps.kelimelik&hl=en_US&gl=US), a Flutter game we learned about through this survey, had 4.5 stars on Google Play with more than 356,000 reviews and 5,000,000 installs at the time of writing! + +<DashImage figure src="images/1gq-r9Mn1BzgKL8XYLnFsEQ.webp" /> + + +### Flutter users want more documentation for game development + +When we asked respondents what had blocked them from making progress in their game projects, we received 31 responses. Many of them are related to lack of documentation: + +* “not enough tutorial and I didn’t understand it enough” + +* “not enough resources/tutorials/documentation (flutter flame)” + +* “The documentation is not very friendly to beginner game devs. There is still much that can be done to create a very clean introduction to Flame components with common scenarios in tutorials or and case study walk-throughs to get the started.” + +This feedback echoes what we learned from the interview study. Survey respondents also mentioned issues such as lacking 3D support, difficulty working with shaders, and performance problems with specific hardware, and visual effects (such as shadows). + +## Recommendations + +Our research results have shown the potential of developing 2D casual games on Flutter. Some Flutter users have tried and succeeded in shipping top-rated games, and many more are interested in getting started in the coming months. + +To set up those new game developers for success, it’s crucial that we position Flutter appropriately in relation to other game development tools on the market, and leverage the open and participatory nature of Flutter to mature its support for game development. + +With that, we’d like to make a few recommendations directed not only to the Flutter team at Google, but also to package authors, tool builders, and content creators from Flutter’s community at large. + +## Market positioning + +The research results suggest three dimensions we need to consider when we position Flutter in the market of game development tools: + +**Types of games**: + +* Targeting 2D casual games is a good starting point. + +* Puzzles and platform games are successful sub-genres of games that have been developed in Flutter. + +**Types of developers**: + +* Flutter app developers who want to leverage the casual game development capabilities of Flutter (such as P02, P04, and P05). + +* Computational artists and graphics enthusiasts (such as P01 and P03), who are attracted by the high degree of control and openness offered by Flutter. + +**Types of game development teams**: + +* We can focus on indie game developers and hybrid game/app agencies that have fewer specialized roles and can benefit more from Flutter’s cross-platform capabilities. + +### Documentation and learning resources + +Based on the user input from the interview study, we believe the creators and educators in the Flutter community can make the most impact by focusing on creating open-source sample games, how-to videos, and curated resource lists for game development. The Flutter Casual Game Toolkit we released at I/O is our first attempt at organizing resources along these lines and raising their visibility among Flutter users. + +### Potential investment in Flutter APIs and capabilities + +During the study, participants pointed out several major product features that they hope the Flutter SDK can better support in the future: + +* Usability and support for shaders ([GitHub issue](https://github.com/flutter/flutter/issues/93800)). Alternatively, it might be enough if library maintainers (for example, Flame) directly use the shader APIs (for example, P03 wants a higher level abstraction in a game engine). + +* Web app bundle size. One participant called out problems with expanding the game to web-based gaming platforms (such as Facebook Instant Game). Because web bundle size is too large, they eventually chose the HTML rendering backend over the faster but larger Canvas Kit backend. (P05) + +* Windows / Steam support. Multiple participants expressed strong interest in trying out the new Windows support for their game. One mentioned that Windows is the most important platform to support, especially if you plan on releasing your game on Steam. (P03) However, this scenario remains ambiguous given that no participants nor our team members have tested game development on Windows or integration with Steam. + +In addition to these three areas of improvements, we’ve recently started collaborating with community members to triage game-related issues and label them as [a: gamedev](https://github.com/flutter/flutter/labels/a%3A%20gamedev). + +## Conclusions + +What we learned from this pair of studies, including Flutter users’ wide interest in game development and Flutter’s simplicity and high degree of control appreciated by existing Flutter game developers, gave the team confidence in pursuing game development as a viable use case. The Casual Game Toolkit is our first investment in this space, and we look forward to hearing how our community members take advantage of it in our follow-up research. + +## Acknowledgement + +[Zoey Fan](https://medium.com/@zoeyfan_43394), [Christopher Fujino](https://twitter.com/chrisfujino), and [JaYoung Lee](https://medium.com/@jayoung.lee) contributed to this research, and [Shams Zakhour](https://medium.com/@shaza) edited this article. We’d like to thank our users who participated in this research and the Flame team for their support in participant recruiting. \ No newline at end of file diff --git a/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1-ehN32qJg_iFuBjuMdUYew.webp b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1-ehN32qJg_iFuBjuMdUYew.webp new file mode 100644 index 0000000000..1d7ce8f7dc Binary files /dev/null and b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1-ehN32qJg_iFuBjuMdUYew.webp differ diff --git a/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/10cJ-ZDludFJ-rcVp92lI8A.webp b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/10cJ-ZDludFJ-rcVp92lI8A.webp new file mode 100644 index 0000000000..addfdaf511 Binary files /dev/null and b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/10cJ-ZDludFJ-rcVp92lI8A.webp differ diff --git a/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1CPLZMvIh93Amt_RvTJ8lPg.webp b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1CPLZMvIh93Amt_RvTJ8lPg.webp new file mode 100644 index 0000000000..6bda7f3eec Binary files /dev/null and b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1CPLZMvIh93Amt_RvTJ8lPg.webp differ diff --git a/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1DDzx3KU38jeAs2gWYc1KVQ.gif b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1DDzx3KU38jeAs2gWYc1KVQ.gif new file mode 100644 index 0000000000..36c4ed37c5 Binary files /dev/null and b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1DDzx3KU38jeAs2gWYc1KVQ.gif differ diff --git a/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1EJba6N3Cb1qa1DUdcc0-kQ.webp b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1EJba6N3Cb1qa1DUdcc0-kQ.webp new file mode 100644 index 0000000000..929005a28c Binary files /dev/null and b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1EJba6N3Cb1qa1DUdcc0-kQ.webp differ diff --git a/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1HyTUK7HxEJfJZofdMGcSJA.gif b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1HyTUK7HxEJfJZofdMGcSJA.gif new file mode 100644 index 0000000000..fb526651e2 Binary files /dev/null and b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1HyTUK7HxEJfJZofdMGcSJA.gif differ diff --git a/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1OOmIZ2plXASJUl49v6TlDA.gif b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1OOmIZ2plXASJUl49v6TlDA.gif new file mode 100644 index 0000000000..505a332e5d Binary files /dev/null and b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1OOmIZ2plXASJUl49v6TlDA.gif differ diff --git a/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1PyYYwZB4TXMqF7W3SHVv0w.jpeg b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1PyYYwZB4TXMqF7W3SHVv0w.jpeg new file mode 100644 index 0000000000..e8a704f56b Binary files /dev/null and b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1PyYYwZB4TXMqF7W3SHVv0w.jpeg differ diff --git a/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1_3mc2w0ZoMF86d5kbuvWwA.webp b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1_3mc2w0ZoMF86d5kbuvWwA.webp new file mode 100644 index 0000000000..5aa52d0139 Binary files /dev/null and b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1_3mc2w0ZoMF86d5kbuvWwA.webp differ diff --git a/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1vFj6DgGFh43jJS6sX2ZxWw.jpeg b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1vFj6DgGFh43jJS6sX2ZxWw.jpeg new file mode 100644 index 0000000000..1c18f53386 Binary files /dev/null and b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/images/1vFj6DgGFh43jJS6sX2ZxWw.jpeg differ diff --git a/sites/www/content/blog/pitching-flutter-to-your-company-or-client/index.md b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/index.md new file mode 100644 index 0000000000..17484284aa --- /dev/null +++ b/sites/www/content/blog/pitching-flutter-to-your-company-or-client/index.md @@ -0,0 +1,218 @@ +--- +title: "Pitching Flutter to your company or client" +description: "The business case for Flutter" +publishDate: 2018-11-01 +author: wmleler1 +image: images/1OOmIZ2plXASJUl49v6TlDA.gif +category: tutorial +layout: blog +--- + +It has been over a year since [What’s Revolutionary about Flutter](https://hackernoon.com/whats-revolutionary-about-flutter-946915b09514) was published (it is still a good introduction). When I wrote that article, few mobile developers had heard about Flutter, but things have come a long way since then. Now I can’t keep up with all the articles and videos being created about Flutter by an active, vibrant, even *exploding* community. [Developers have discovered Flutter](https://twitter.com/timsneath/status/1024684022574669824), and [they *love it*](https://medium.com/flutter-io/what-weve-learned-from-the-july-2018-flutter-user-survey-cbbf1e04370c). Here’s an animated GIF of just one of the [wonderful apps created using Flutter](https://itsallwidgets.com/), by [Reflectly](https://medium.com/reflectly-engineering/reflectly-from-react-native-to-flutter-2e3dffced2ea): + +<DashImage figure src="images/1OOmIZ2plXASJUl49v6TlDA.gif" alt="Reflectly intelligent journaling Flutter app" caption="Reflectly intelligent journaling Flutter app" /> + + +Developers often tell me that one of their biggest challenges now is convincing their company management — or if they are an agency or freelancer, convincing their clients — to try Flutter. These decision-makers are not as eager to adopt the latest technology, just for technology’s sake. They are typically tech savvy, but that is not their only concern. They want to know if using something new will help their company succeed; for example, by bringing in new customers or reducing risks. + +This article is aimed at two groups: + +* Developers who want to help their management understand why they should use Flutter. Even if a developer is familiar with Flutter, they still need solid arguments to present to management. + +* Developers who want to decide for themselves whether to develop a product with Flutter. + +This article will also discuss reasons why a specific mobile app might not be a good target for Flutter, so an informed decision can be made. + +## What is Flutter? + +The tagline for Flutter (kind of a written elevator pitch) is +> **Build beautiful native apps in record time** + +Let’s break this tagline up into four parts and discuss them one by one: + +1. Build + +1. Beautiful + +1. Native apps + +1. In record time + +## 1. Build + +Flutter is currently focused on building mobile apps for iOS and Android. + +However, there is a larger, longer-term vision that sets Flutter apart from current mobile frameworks: Flutter is not just a *framework*, it is a *complete SDK* for building apps that interact using a screen. That means that Flutter contains everything needed to create a user interface, including the renderer and the things to be rendered (which Flutter calls **widgets**). + +In many ways, Flutter is similar to game engines such as [Unity](https://unity3d.com/) or [Unreal](https://www.unrealengine.com/), which also supply their own renderer and the things to be rendered (and other software). But instead of building *games*, Flutter builds *apps*. + +The fact that Flutter is a complete SDK means that it can be ported to run on virtually any hardware that includes a display. The Flutter renderer uses [Skia, a popular open-source graphics engine](https://en.wikipedia.org/wiki/Skia_Graphics_Engine) that is easy to use on different devices. + +<DashImage figure src="images/1_3mc2w0ZoMF86d5kbuvWwA.webp" alt="Flutter on desktop and Raspberry Pi" caption="Flutter on desktop and Raspberry Pi" /> + + +While at Google we have been focused on mobile apps, various third parties have done ports of Flutter to [desktop computers](https://github.com/google/flutter-desktop-embedding) (macOS shown at left, Windows, Linux, and [others](https://groups.google.com/forum/?pli=1#!forum/flutter-desktop-embedding-dev)), to [TVs](https://groups.google.com/forum/#!topic/flutter-dev/0Yf-b8fwGZs) (here’s a [video of Flutter running on an Nvidia Shield TV](https://youtu.be/crODae5bIew)), and to the Raspberry Pi (shown at bottom left). Internally, Flutter is also used to build user interfaces for [Fuchsia](https://github.com/fuchsia-mirror). + +Increasingly, apps will expand beyond mobile phones. It will be common to access apps across multiple devices, including home assistants (Google Home Hub, Lenovo Smart Display, etc.), interactive displays in cars, home appliances (refrigerators), wearables (watches, clothing), and other [IoT](https://en.wikipedia.org/wiki/Internet_of_things) devices. In 2017 there were 8.4 billion such devices online. By 2020 (less than two years from now) [IDC estimates](https://www.businesswire.com/news/home/20131003005687/en/Internet-Poised-Change-IDC) that there will be over 30 billion (compared to 5 billion mobile phones). Many of these devices will have interactive screens. Flutter’s architecture has all the ingredients to create [powerful and beautiful user interfaces for these new devices](https://www.forbes.com/sites/forbesnycouncil/2018/07/26/why-you-should-start-looking-at-googles-flutter-and-fuchsia-now/#228e36cca309). + +<DashImage figure src="images/10cJ-ZDludFJ-rcVp92lI8A.webp" alt="Screens everywhere!" caption="Screens everywhere!" /> + + +And of course, Flutter is completely free and open source. + +## 2. Beautiful + +How can you ensure the mobile app you are creating will succeed when it is competing against [3.8 million apps in the Google Play Store and 2 million apps in the Apple App Store](https://www.statista.com/statistics/276623/number-of-apps-available-in-leading-app-stores/)? Even if you manage to convince a user to download your app, there is only a 3% chance that they will be actively using it 30 days later. Gartner Inc. says that [only 0.01% of all published mobile apps become a financial success](https://www.forbes.com/sites/connieguglielmo/2014/01/13/mobile-apps-may-not-pave-the-way-to-developer-riches-sales-average-less-than-1250-a-day/#78025d324890). Developers need all the help they can get. + +<DashImage figure src="images/1vFj6DgGFh43jJS6sX2ZxWw.jpeg" alt="Alibaba (left) and Topline (right)" caption="Alibaba (left) and Topline (right)" /> + + +<DashImage figure src="images/1HyTUK7HxEJfJZofdMGcSJA.gif" alt="2Dimensions" caption="2Dimensions" /> + + +Studies have shown that having an [appealing design can be very important](https://medium.com/coffee-and-junk/design-psychology-aesthetic-usability-effect-494ed0f22571). If you look at the most popular mobile apps from the last few years, they each have their own design aesthetic. In addition, beautiful apps win awards, which provides valuable publicity. + +As just a few examples, the Flutter apps shown to the left include apps [from Alibaba](https://hackernoon.com/making-the-most-of-flutter-from-basics-to-customization-433171581d01) (the largest e-commerce company in the world), the [Topline recording app](https://www.abbeyroad.com/apps) from Abbey Road Studios, and a demo app of amazing [realtime animations from 2Dimensions](https://apkpure.com/2dimensions-showcase-unreleased/com.two_dimensions.showcaseapp). + +Increasingly, companies want their mobile apps to complement their company branding, just like they do for their website and web apps. This requires a *high degree of customization*. + +Finally, designers often have great ideas that — by the time they are implemented — are badly watered down because of toolkit limitations. Flutter helps you *say yes to your designer* to build apps that implement their vision. + +You can see more Flutter apps in the [Flutter showcase](https://flutter.io/showcase/) (and hundreds more on the [It’s All Widgets site](https://itsallwidgets.com/)). This includes the beautiful [Reflectly journaling app](https://reflectly.app/), shown at the beginning of this article using an animated GIF, but you should install it from the app stores onto your [Android](https://play.google.com/store/apps/details?id=com.reflectlyApp) or [Apple](https://itunes.apple.com/us/app/reflectly-mindfulness-journal/id1241229134) phone. Be sure to read about [their experiences switching their app to Flutter](https://medium.com/reflectly-engineering/reflectly-from-react-native-to-flutter-2e3dffced2ea). + +## 3. Native apps + +Mobile developers might be surprised by this point. In the world of mobile apps, the term “native app” is often used to refer to an app that targets platform APIs directly using specific languages. And just to make things more confusing, frameworks like React Native and Xamarin use the term “native” to mean that they can use the platform widgets. + +Other areas of computing don’t really use the term “native” as it is used in mobile. For example, you can install Linux on either Windows and macOS computers (and many other kinds), but you wouldn’t call Windows or macOS “native” and Linux a “cross-platform” or “hybrid” solution. People consider Linux just as native as Windows or macOS, *because it is*. + +A more precise [definition of “native” for computing](https://en.oxforddictionaries.com/definition/native) is: +> “Designed for or built into a given system, **especially denoting the language associated with a given processor**, computer, or compiler, and programs written in it.” + +Flutter apps are compiled to native machine ([ARM](https://en.wikipedia.org/wiki/ARM_architecture)) code on both iOS and Android. + +The advantages of native apps are plentiful. Native apps start up faster, and most importantly they are smoother and less likely to suffer from jerkiness or stutter (*jank is not beautiful*). Native apps also give the developer more control over the behavior of their app. + +In order to receive the advantages of native apps, mobile developers have typically built two separate apps using separate tools and languages. Often, this means having distinct teams of developers for each platform, who then have to coordinate with each other. This raises costs significantly, increases risks, and slows the time to market. Flutter allows developers to consolidate into a single codebase, unify their development teams, reduce risk, and speed time-to-market. All while getting the benefits of a native app. + +<DashImage figure src="images/1EJba6N3Cb1qa1DUdcc0-kQ.webp" alt="Which one is “native”? Both!" caption="Which one is “native”? Both!" /> + + +What about the widgets? Because [Flutter provides its own widgets](https://medium.com/flutter-io/why-flutter-doesnt-use-oem-widgets-94746e812510), you might be afraid that Flutter apps will look or feel uncannily different than apps developed using the platform widgets and tools. + +To the left, the iOS platform settings screen is compared to the same screen created using Flutter. Even though there are a few small differences at the pixel level, the bottom line is that Flutter makes it easy to create screens that look and feel like platform screens. + +Because platforms change between different OS versions, even a platform native app will typically look different on distinct versions of an OS. It is virtually meaningless for Flutter to try to be “pixel perfect”. + +Flutter’s widgets adapt to the platform they are running on to provide the proper look and feel, including icons, colors, layout, fonts, scrolling behavior, and so forth. Instead of being “pixel perfect” a more important goal for a Flutter app is to conform to the design guidelines for the platform, and to feel familiar and comfortable to the user. + +The high platform fidelity of Flutter apps is possible because Flutter is a complete SDK that renders directly to the platform canvas. Your app does not depend on the platform’s widgets, fonts, etc. that might change out from underneath it. + +Flutter also avoids the need for [compatibility libraries](https://developer.android.com/topic/libraries/support-library/). For example, here is a Flutter app that uses Material Design widgets, on a phone running Android Jelly Bean (4.1.2). This phone came out *two years before* Material Design was even created, so the phone has no built-in widgets for Material Design at all. But your Flutter app will run the same as it does on a more modern phone with a more recent OS, and look great. + +<DashImage figure src="images/1-ehN32qJg_iFuBjuMdUYew.webp" /> + + +Without Flutter, you would have to deal with these problems yourself. This means more testing on older versions of the OS, and workarounds for platforms that don’t provide features that your app uses. Developers tell us that testing is dramatically easier using Flutter. + +In summary, Flutter not only provides the advantages of native apps, it provides additional important benefits: + +* Flutter apps will run on older versions of each platform OS. For example, if you, the developer, test your Flutter app on a recent version of Android, it will look the same on older versions. This allows Flutter apps to run well on older phones, with far less testing. + +* New releases of the platform OS will also never break your app, unless a change breaks something on which Flutter depends (this is extremely rare, and would be a bug in Flutter, not in your app). + +* Your app will not look dramatically different because of modifications made to the OS made by manufacturers or carriers (a common example is changes to the default fonts). + +* Most importantly, Flutter gives you, the developer, complete control over what your app looks like, *down to the pixel*, on all platforms and versions. + +* If desired, you can explicitly make a Flutter app look different on iOS and Android. Even if you don’t do this, the Flutter widgets adapt to the design guidelines for each platform. + +* Flutter can do this using a single codebase for both platforms. Or you can [add platform native code for each platform](https://flutter.io/platform-channels/) to a Flutter app, if desired. + +## 4. In record time + +Last, but certainly not least, Flutter lets you develop *better* apps *faster*. The most popular (and even hyped) feature of Flutter is its *stateful hot reload*. Not only is it amazingly fast (usually sub-second), it is stateful, which means if you change some code deep down in your app, after you recompile you don’t have to navigate back into the same place or manually recreate your state in order to see the effect of the change. + +<DashImage figure src="images/1DDzx3KU38jeAs2gWYc1KVQ.gif" alt="Stateful hot reload!" caption="Stateful hot reload!" /> + + +Flutter’s stateful hot reload is made possible by advanced compiler technology (developed by some of the same people who built the [V8 compiler](https://en.wikipedia.org/wiki/Chrome_V8) for JavaScript and [Strongtalk](https://en.wikipedia.org/wiki/Strongtalk) for Smalltalk) and the fact that Flutter is native and based on reactive views. + +In addition to speeding things up, many developers report that Flutter dramatically changes the way they code. They can try out new ideas and new layouts quickly. They can make changes requested by their stakeholders right in front of them. + +We also find that it helps designers get exactly what they want. They can play with various parameters until it looks just right. Indeed, designers who know how to use CSS in web apps tell us that it is [easy for them to learn](https://youtu.be/BJCqRpvvTrM) how to make changes themselves to layouts in Flutter . + +To really appreciate this, [watch a Flutter app live coded](https://youtu.be/iflV0D0d1zQ) in real time, or try Flutter yourself to build a real app. Join a Flutter hackathon and see how quickly you can learn Flutter and build a working app in it. + +As discussed in the last section, Flutter apps also require less testing, which means that you can test and deliver new features for your app much more quickly. For example, JD.com tells us: +> We were able to develop the same features with half of the development resource. With the same number of engineers, each release can now have more features. + +Likewise, [Alibaba](https://hackernoon.com/making-the-most-of-flutter-from-basics-to-customization-433171581d01) tells us that using Flutter, the average time to add a new feature went from a month down to two weeks. + +<DashImage figure src="images/1CPLZMvIh93Amt_RvTJ8lPg.webp" alt="Hamilton, the app" caption="Hamilton, the app" /> + + +In [the Hamilton app](https://youtu.be/kfd-oLypqFI), the developers actually made a major change to the app *the night before* they released it, with the confidence that the app would be rock solid. That app, built in three short months, was featured on both app stores. Plus Flutter makes it easier for them continue to add new features frequently, to keep their users active and coming back for more. + +That’s how you build a successful mobile app. + +<DashImage figure src="images/1PyYYwZB4TXMqF7W3SHVv0w.jpeg" /> + + +Not only is it easy and fast to build and modify your apps, it is just as fast to update the Flutter widgets. A common concern about Flutter not using the platform widgets is that Flutter will struggle to keep up with the platforms as they release new widgets or improve old ones. + +However, when Apple announced their new iPhone X with its notch, Flutter was able to add support for the notch before the phone itself was released. + +And this doesn’t only apply to iOS features. When Google announced a [significant revamp of Material Design](https://material.io/design/material-theming/) at I/O 2018, Flutter was already updated to fully implement the new features. The ability of Flutter to be quickly customized made this possible. + +The bottom line is that developers frequently report that they are between two and three times more productive using Flutter, and that they build better apps that are more successful. + +## Risks and limitations + +There are always a few tradeoffs, and Flutter is no exception. Here are some reasons why Flutter may not be the best tool for a specific app, so that you can make an informed decision. + +Because Flutter apps include the Flutter widgets and renderer, this makes the resulting apps slightly larger than apps that utilize the platform widgets and renderer. Up until recently, a Flutter app had a minimum size of 6.7MB, but that has been [reduced to a little over 4MB](https://twitter.com/flutter_wm/status/1025747957608411137). We plan to make further optimizations in the future. + +One of the major benefits of Flutter is that a single codebase can be used to release apps on both mobile platforms (although Flutter is also used to build apps for only a single platform). However, there are some apps that are tightly tied to one specific platform or are primarily a wrapper around a platform-provided view, such as an app that is mainly a background service to provide notifications. These apps can be written in Flutter, but they will not see as much benefit from it. + +A question we are often asked is whether Flutter is here to stay. There are multiple reasons why the answer is an *emphatic yes*. First, Google is itself a heavy user of Flutter, including for both customer-facing and internal apps (with more apps being developed). For example, the recently released Google Ads mobile app (formerly called AdWords) is written in Flutter. Google is solidly behind Flutter and is committed to its success. + +Second, the fact that major apps have typically been built on two separate platforms means that companies have often considered it less risky to (for example) first build an iOS app, and then build an Android app later if there is enough demand. With Flutter, [apps can be released simultaneously on both platforms](https://medium.com/flutter-community/porting-a-75-000-line-native-ios-app-to-flutter-57c6571c57b4) with virtually identical features, circumventing that risk and increasing the potential market for an app. This can be a big benefit to Android. + +Another potential issue is that Flutter is comparatively new. It will naturally take a little time for Flutter to catch up to the incumbent tools, both in terms of features and community support. Presently, there are some features provided by the platforms that will take some time for Flutter to implement. Google is putting the resources into Flutter to add these features. + +If you want deeper information on why it is worthwhile to try Flutter, read [What’s Revolutionary about Flutter](https://hackernoon.com/whats-revolutionary-about-flutter-946915b09514) and [Why Flutter uses Dart](https://hackernoon.com/why-flutter-uses-dart-dd635a054ebf). And read the results of [our latest user study](https://medium.com/flutter-io/what-weve-learned-from-the-july-2018-flutter-user-survey-cbbf1e04370c) to see what users of Flutter think about it. + +## How to get started + +When you are ready to try Flutter, here some helpful things: + +1. [https://flutter.io/get-started/](https://flutter.io/get-started/) — this guide helps you install Flutter, configure your editor, go for a test drive, and write your first app. + +1. Watch some [Flutter videos on YouTube](https://www.youtube.com/results?search_query=Flutter). + +1. There are a set of guides for devs who are familiar with one (or more) of the following: [Android](https://flutter.io/flutter-for-android/), [iOS](https://flutter.io/flutter-for-ios/), [React Native](https://flutter.io/flutter-for-react-native/), [Xamarin](https://flutter.io/flutter-for-xamarin-forms/). + +1. Or if you are new to mobile development, there are also tutorials, such as: [Building Layouts in Flutter](https://flutter.io/tutorials/layout/), [Add Interactivity](https://flutter.io/tutorials/interactive/), and [A Tour of the Flutter Widget Framework](https://flutter.io/widgets-intro/) (including reactive principles, if you are not familiar with them). + +1. A valuable resource is the [Flutter cookbook](https://flutter.io/cookbook/). Also the [Widget catalog](https://flutter.io/widgets/) and [the FAQ](https://flutter.io/faq/). + +1. Similarly, look at the apps in [It’s All Widgets](https://itsallwidgets.com/). Many of these are open source, so you can see code for published apps. And once you write your own Flutter apps, be sure to submit them. + +1. To get a list of the best articles about Flutter delivered to your inbox each week, [subscribe to Flutter Weekly](https://flutterweekly.net/). + +1. There are a bunch of lovely [codelabs for Flutter](https://codelabs.developers.google.com/?cat=Flutter), and if that isn’t enough there is a [free online video course on Flutter](https://www.udacity.com/course/build-native-mobile-apps-with-flutter--ud905) from Udacity. + +1. And [plenty of other documentation here](https://flutter.io/get-started/learn-more/). + +To get involved in the Flutter community, you can find us on [Twitter](https://twitter.com/flutterio), [Gitter](https://gitter.im/flutter/flutter), and [Stack Overflow](https://stackoverflow.com/tags/flutter). You should subscribe to the [Flutter Dev mailing list](https://groups.google.com/forum/#!forum/flutter-dev). And find a local Flutter Meetup or Study Jam, and see if there are any mobile app hackathons happening near you. + +## Conclusions + +Flutter provides high-velocity development that makes you more productive and helps you build better apps. It is an expressive, highly flexible, and customizable toolkit that gives you, the developer, awesome amounts of control. And it creates fast and smooth native iOS and Android apps from a single codebase. + +Flutter will save you money and reduce risks. It is free and open source. Flutter will also help you make more money. You can go after both the Android and iOS markets simultaneously, and create better apps in less time. + +Developers also say (*and this is one of my favorite things*) that [Flutter makes mobile development fun again](https://medium.com/@matthew.smith_66715/why-we-chose-flutter-and-how-its-changed-our-company-for-the-better-271ddd25da60). An outstanding [92% of developers](https://medium.com/flutter-io/what-weve-learned-from-the-july-2018-flutter-user-survey-cbbf1e04370c) who use Flutter say they are *satisfied* or *very satisfied* with it. This number has been steadily increasing, and remember that these results are all from the alpha and beta versions of Flutter, *before* the 1.0 release. + +Finally, Flutter looks to the future. It is the only native mobile toolkit that supports reactive views, a programming paradigm that helps build better apps, and also enables things like super-fast stateful hot reload. And because it is a complete SDK, Flutter can remain relevant by being ported to new platforms as they become popular. \ No newline at end of file diff --git a/sites/www/content/blog/platform-channel-examples/images/15PoXySK4-tAKWAY1zCzfHg.webp b/sites/www/content/blog/platform-channel-examples/images/15PoXySK4-tAKWAY1zCzfHg.webp new file mode 100644 index 0000000000..5c8f0bdc75 Binary files /dev/null and b/sites/www/content/blog/platform-channel-examples/images/15PoXySK4-tAKWAY1zCzfHg.webp differ diff --git a/sites/www/content/blog/platform-channel-examples/images/1LSMSOh-0dz8TCaKP2Z-CoQ.webp b/sites/www/content/blog/platform-channel-examples/images/1LSMSOh-0dz8TCaKP2Z-CoQ.webp new file mode 100644 index 0000000000..516f0792cb Binary files /dev/null and b/sites/www/content/blog/platform-channel-examples/images/1LSMSOh-0dz8TCaKP2Z-CoQ.webp differ diff --git a/sites/www/content/blog/platform-channel-examples/images/1mc9V6dDcmV0qT6UpzX1AQg.webp b/sites/www/content/blog/platform-channel-examples/images/1mc9V6dDcmV0qT6UpzX1AQg.webp new file mode 100644 index 0000000000..00e186ba27 Binary files /dev/null and b/sites/www/content/blog/platform-channel-examples/images/1mc9V6dDcmV0qT6UpzX1AQg.webp differ diff --git a/sites/www/content/blog/platform-channel-examples/images/1oXELli8oLVJuWFW6BtdXjw.webp b/sites/www/content/blog/platform-channel-examples/images/1oXELli8oLVJuWFW6BtdXjw.webp new file mode 100644 index 0000000000..4e5cd4eb1b Binary files /dev/null and b/sites/www/content/blog/platform-channel-examples/images/1oXELli8oLVJuWFW6BtdXjw.webp differ diff --git a/sites/www/content/blog/platform-channel-examples/images/1raON7YDig_i1Lt88IsfjFA.gif b/sites/www/content/blog/platform-channel-examples/images/1raON7YDig_i1Lt88IsfjFA.gif new file mode 100644 index 0000000000..a491325490 Binary files /dev/null and b/sites/www/content/blog/platform-channel-examples/images/1raON7YDig_i1Lt88IsfjFA.gif differ diff --git a/sites/www/content/blog/platform-channel-examples/index.md b/sites/www/content/blog/platform-channel-examples/index.md new file mode 100644 index 0000000000..2fc2b9f6e9 --- /dev/null +++ b/sites/www/content/blog/platform-channel-examples/index.md @@ -0,0 +1,112 @@ +--- +title: "Platform channel examples" +description: "Google Summer of Code (GSoC) ‘20" +publishDate: 2020-08-29 +author: ayushbherwani1998 +image: images/15PoXySK4-tAKWAY1zCzfHg.webp +category: events +layout: blog +--- + +Google Summer of Code (GSoC) 2020 + +<DashImage figure src="images/15PoXySK4-tAKWAY1zCzfHg.webp" /> + + +Google Summer of Code is an initiative by Google to promote open source among student developers, where students pair with a mentor and work with an open source organization for three months. + +This year marks the 16th year of Google Summer of Code, and the first year of the Dart organization taking part in GSoC. Many students might be thinking, that’s great, but where is Flutter? + +<iframe src="https://giphy.com/gifs/3ogwFTfjbRk2iz89Mc/html5" width="435" height="244" frameborder="0" allowfullscreen></iframe> + + +The good news is, the projects listed include both Flutter and Dart projects. (And to be clear, all Flutter projects are written in the Dart language, but not all Dart projects use the Flutter framework.) Check out all of the project ideas on the [Dart wiki](https://github.com/dart-lang/sdk/wiki/Dart-GSoC-2020-Project-Ideas). + +This post showcases the final project, which demonstrates 3 different kinds of platform channels, and was written during the 3-month coding period, as well as an example of a federated plugin. + +The completed Platform Channel sample demonstrates how to use different kinds of named channels to communicate with the host application, specifically: + +* How to use `MethodChannel` to invoke platform methods. + +* How to use `EventChannel` to listen to continuous value changes from the platform. + +* How to use `BasicMessageChannel` and `MessageCodec` to send messages of different types across the platform. + +You can find the complete [platform channel sample](https://github.com/flutter/samples/tree/master/platform_channels) on GitHub. + +## **PlatformChannel sample** + +I was selected to create the `PlatformChannel` sample app, which aims to demonstrate how to send data back and forth in messages between Dart and native code using platform channels. + +Flutter is a UI framework that allows you to paint beautiful pixels on the screen using the same Dart code across platforms, but what if you want to work with native APIs to access sensor events, hardware information, or access hardware like a camera, or biometrics? *Platform channels* are here to help. It’s a mechanism that allows you to communicate with your native host application and access the platform-specific APIs. + +### MethodChannel **demo** + +The MethodChannel demo demonstrates how to implement a [`MethodChannel`](https://api.flutter.dev/flutter/services/MethodChannel-class.html) and invoke platform methods to increment and decrement the value of a counter. + +Related PR: [#453](https://github.com/flutter/samples/pull/453) + +<DashImage figure src="images/1raON7YDig_i1Lt88IsfjFA.gif" alt="MethodChannel demo in action" caption="MethodChannel demo in action" /> + + +### EventChannel demo + +The EventChannel demo demonstrates how to implement an [`EventChannel`](https://api.flutter.dev/flutter/services/EventChannel-class.html) to listen to continuous values from the device’s `Accelerometer` sensor. It also shows how to test the `EventChannel` implementation, which gives you a deeper understanding of how `EventChannel` works. + +Related PR: [#462](https://github.com/flutter/samples/pull/462) + +<DashImage figure src="images/1LSMSOh-0dz8TCaKP2Z-CoQ.webp" alt="EventChannel Demo in action" caption="EventChannel Demo in action" /> + + +### Platform Image demo + +The Platform Image demo demonstrates how to use [`BasicMessageChannel`](https://api.flutter.dev/flutter/services/BasicMessageChannel-class.html) with [`StandardMessageCodec`](https://api.flutter.dev/flutter/services/StandardMessageCodec-class.html) to load an image from native assets. It also solves [issue #20879: Would like an example of sending an image across `PlatformChannels](https://medium.com/r?url=https%3A%2F%2Fgithub.com%2Fflutter%2Fflutter%2Fissues%2F20879).` + +Related PR: [475](https://github.com/flutter/samples/pull/475) + +<DashImage figure src="images/1mc9V6dDcmV0qT6UpzX1AQg.webp" alt="Platform Image demo in action" caption="Platform Image demo in action" /> + + +### BasicMessageChannel & different MessageCodec demo + +The BasicMessageChannel demo demonstrates how to use `BasicMessageChannel` and [`MessageCodec`](https://api.flutter.dev/flutter/services/MessageCodec-class.html) to move structured data across platforms. In the demo, it uses a variety of `MessageCodecs` to send and receive data about pets. + +Related PRs: [#484](https://github.com/flutter/samples/pull/484), [#496](https://github.com/flutter/samples/pull/496) + +<DashImage figure src="images/1oXELli8oLVJuWFW6BtdXjw.webp" alt="BasicMessageChannel Demo in action" caption="BasicMessageChannel Demo in action" /> + + +## Federated plugin sample + +During my first video conference with my mentor, Andrew, he told me about an often-used quote at Google, to “always keep your wheels in the dirt”; in other words, “make sure that you aren’t stuck, but can make progress and move forward”. So, to keep my wheels in the dirt during GSoC, we decided that I would *also* work on a federated plugin sample. The sample aims to demonstrate how to build a *federated plugin* (a plugin divided into a different package for each platform implementation) to retrieve the current battery level of the device on different platforms. The sample also demonstrates how to work with platform channels to communicate with different platforms, including web and desktop. Currently, the plugin only supports Android and web. Support for Windows and Linux will be added soon. + +Check out the [federated_plugin sample on GitHub](https://github.com/flutter/samples/tree/master/experimental/federated_plugin). + +Related PRs: [#497](https://github.com/flutter/samples/pull/497), [#503](https://github.com/flutter/samples/pull/503), [#507](https://github.com/flutter/samples/pull/507), [#526](https://github.com/flutter/samples/pull/526) + +Check out all of [my PRs for flutter/samples](https://github.com/flutter/samples/pulls?q=+is%3Apr+author%3AAyushBherwani1998+). + +### **Other Contributions** + +Apart from working on the main project and contributing to the [flutter/samples](https://github.com/flutter/samples) repository, I also contributed to the [flutter/flutter](https://github.com/flutter/flutter) repository, and it has been a fabulous learning journey for me. During the GSoC period, I opened 20 pull requests, of which 16 got merged and three are still in review. Check out [my PRs to flutter/flutter](https://github.com/flutter/flutter/pulls?q=is%3Apr+author%3AAyushBherwani1998+). + +Taking the first step and contributing to any open source project is often challenging. We all have encountered this situation, therefore to help the first time contributors and share my experience, I penned a blog, [Contributing to Flutter: Getting Started](https://medium.com/flutter-community/contributing-to-flutter-getting-started-a0db68cbcd5b). + +## **Experience** + +It has been a great journey to work and learn under the mentorship of the Flutter team. For me, the best thing about GSoC is not the stipend but the mentorship and learning experience that I can cherish my whole life. + +### **Mentorship** + +[Andrew](https://twitter.com/redbrogdon) has not only been a great guide but also a friend who has helped me to improve my technical skills and with my career-related questions. He always trusted my abilities and allowed me to explore and experiment with the samples. Whenever I was stuck on a problem, he proactively helped me to find a way around, or introduced me to someone from the Flutter team who could help me. The same goes for [Brett](https://twitter.com/DomesticMouse). Though not my primary mentor, he helped me whenever I reached out to him. + +### **Learning** + +I am glad Andrew and I decided to follow the [Test Driven Development](https://en.wikipedia.org/wiki/Test-driven_development) process, which has helped me to understand a different type of software development and get a better understanding of how platform channels work internally. Working on a federated plugin sample also gave me a chance to explore win32 development. + +### **Challenges** + +Challenges are what makes GSoC a great learning journey. One of the hurdles we faced was designing the WinRT/C++ API for the federated plugin sample. Though we were not able to overcome it due to some constraints, doing research about the topic helped me to explore the other paths and find an alternate solution. +> # That’s it for this article, I would like to thanks Andrew, Brett, and the Flutter team, who helped me to complete the samples and contribute to the Flutter SDK 🎉 + +*About the author: Ayush is an Undergraduate student who is passionate about developing efficient Mobile apps in both Flutter and Android. Apart from Mobile Development, his interest lies in developing Mobile DApps. You can connect with him on[ Twitter](https://twitter.com/ayushbherwani),[ LinkedIn](https://linkedin.com/in/ayushbherwani), and[ GitHub](https://github.com/AyushBherwani1998).* \ No newline at end of file diff --git a/sites/www/content/blog/playful-typography-with-flutter/images/0-N5jTlkJHFmEeb0e.webp b/sites/www/content/blog/playful-typography-with-flutter/images/0-N5jTlkJHFmEeb0e.webp new file mode 100644 index 0000000000..802037d4da Binary files /dev/null and b/sites/www/content/blog/playful-typography-with-flutter/images/0-N5jTlkJHFmEeb0e.webp differ diff --git a/sites/www/content/blog/playful-typography-with-flutter/images/090hPGzJLj7qBZp7t.webp b/sites/www/content/blog/playful-typography-with-flutter/images/090hPGzJLj7qBZp7t.webp new file mode 100644 index 0000000000..be270ee574 Binary files /dev/null and b/sites/www/content/blog/playful-typography-with-flutter/images/090hPGzJLj7qBZp7t.webp differ diff --git a/sites/www/content/blog/playful-typography-with-flutter/images/0pO_MkjR1GrLhb8va.webp b/sites/www/content/blog/playful-typography-with-flutter/images/0pO_MkjR1GrLhb8va.webp new file mode 100644 index 0000000000..bd7346ae16 Binary files /dev/null and b/sites/www/content/blog/playful-typography-with-flutter/images/0pO_MkjR1GrLhb8va.webp differ diff --git a/sites/www/content/blog/playful-typography-with-flutter/images/0x9XqN84kLr7s86vK.webp b/sites/www/content/blog/playful-typography-with-flutter/images/0x9XqN84kLr7s86vK.webp new file mode 100644 index 0000000000..04b3344a6b Binary files /dev/null and b/sites/www/content/blog/playful-typography-with-flutter/images/0x9XqN84kLr7s86vK.webp differ diff --git a/sites/www/content/blog/playful-typography-with-flutter/index.md b/sites/www/content/blog/playful-typography-with-flutter/index.md new file mode 100644 index 0000000000..af0653faff --- /dev/null +++ b/sites/www/content/blog/playful-typography-with-flutter/index.md @@ -0,0 +1,85 @@ +--- +title: "Playful typography with Flutter" +description: "Exploring variable fonts and shaders" +publishDate: 2023-01-20 +author: hellobrianjames +image: images/0x9XqN84kLr7s86vK.webp +category: announcements +layout: blog +--- + +## Intro + +I’m Brian, a newcomer to the Flutter-verse, and I’ve been a UX engineer on Google’s Flutter team for about six months now. As a UX engineer, I combine my background as a graphic designer with coding skills to help the team build Flutter itself, and to show off what Flutter can do. This blog post shares one of my first experiments with Flutter as a hybrid designer–developer: a simple typography-themed puzzle app. It combined my creative background in design with technical explorations around a couple of Flutter APIs you might not have tried yet: [FontVariation](https://api.flutter.dev/flutter/dart-ui/FontVariation-class.html) and [AnimationController](https://api.flutter.dev/flutter/animation/AnimationController-class.html). I had a lot of fun creating this app, and through it I saw first-hand how great Flutter is for designers who code, or developers with an eye for design. + +<DashImage figure src="images/0x9XqN84kLr7s86vK.webp" alt="*A palette of customized Flutter text widgets, using variable fonts and shaders.*" caption="*A palette of customized Flutter text widgets, using variable fonts and shaders.*" /> + + +## Typography + +Graphic designers love geeking out on “typography,” which is just a fancy word for the styling of text. This includes things like choosing fonts, sizing, layout, spacing, and so on. With all of these settings to work with, letters can become a kind of art form in themselves! Being a designer by training, I knew whatever I created for this exploratory project would somehow involve digging into typography with Flutter. In the past few years, [variable fonts](https://fonts.google.com/knowledge/introducing_type/introducing_variable_fonts) have emerged as an intriguing way for designers to have unprecedented control over text styling. It’s an area I’ve been meaning to explore more, and with Flutter’s support for variable fonts, it seemed like the perfect opportunity. + +You’re probably already familiar with fonts in general, which determine what each character in a block of text looks like. Normal fonts usually give us a few settings that we can choose from, for instance weight: bold, normal, or light. Variable fonts take that customization to the next level. In the case of weight, for instance, we might be able to pick any numerical setting between 100 (extremely light) and 1000 (extremely bold). And we aren’t limited only to weight; font creators can allow users to set all kinds of things, like the width of the letters, or depth of the descenders (how far lowercase letters like ‘p’ and ‘y’ drop down), and much more. Each of these different settings is called an “axis,” like the axes of a graph. + +<DashImage figure src="images/0pO_MkjR1GrLhb8va.webp" alt="*The letter ‘g’ in the variable font Roboto Flex, with variations along the width axis (left to right) and weight axis (top to bottom). These are just two of its many axes in effect!*" caption="*The letter ‘g’ in the variable font Roboto Flex, with variations along the width axis (left to right) and weight axis (top to bottom). These are just two of its many axes in effect!*" /> + + +Flutter offers a straightforward way to adjust these settings in the `TextStyle` widget, by using the `fontVariations` field. For example, the following code snippet would create a text style in the Roboto Flex variable font, at a size of 18, with variable axis settings for the weight (`wght`) and width (`wdth`): + +```less +TextStyle( + fontFamily: 'RobotoFlex', + fontSize: 18, + fontVariations:[ + FontVariation('wght', 374), + FontVariation('wdth', 118) + ], +) +``` + + +For more information on variable fonts, check out the [variable fonts resources](https://fonts.google.com/knowledge/topics/variable_fonts) on the Google Fonts site. + +Setting the variations was easy enough, but then I wanted to animate from one variation setting to another, and between combinations of settings. Fortunately, Flutter and Dart made it easy to take two existing underlying features ([`FontVariation`](https://api.flutter.dev/flutter/dart-ui/FontVariation-class.html) and [`AnimationController`](https://api.flutter.dev/flutter/animation/AnimationController-class.html)), and glue them together to make my own new custom motion typography effect. + +For my demo, I made a `WonkyChar` widget and a `WonkyAnimPalette` helper class, which gave me a simple way to choose any letters to show on screen, control the text size, and feed in a mix of settings related to variable font axes. The `WonkyChar` widget also contained a standard Flutter `AnimationController` object, which I used to animate the settings. In this example, the letter ‘M’ will show at a size of 200, and animate its weight (boldness) from very thin to very bold, in a four-second animation: + +```less +WonkyChar( + text: 'M', + size: 200, + animationDurationMillis: 4000, + animationSettings: [ + WonkyAnimPalette.weight( + from: 100, + to: 900, + ), + ], +), +``` + + +## Custom shaders + +The effect of the variable fonts, especially with animation, was great, but I wanted to push my visual creativity even further and see what I could do with the letter shapes. Incidentally, I had heard that Flutter just added support for custom fragment shaders. Shaders are programs that run on the computer’s Graphics Processing Unit (GPU), allowing developers to create all kinds of visuals while maintaining high frame rates. Flutter supports writing shaders in GLSL, one of the most well-known and well-documented shader languages, with many tutorials and examples available in books, websites, YouTube videos, and massive open online courses (MOOCs). I’m definitely not an expert in shaders or graphics programming, but I’ve tinkered with them before, so this was another great opportunity to explore Flutter’s features while building my own skills. +> **Note**: Shaders are a fairly advanced coding topic, and the details about plugging them into your Flutter app are beyond the scope of this blog post. If you’d like to try writing some shaders yourself, check out [Writing and using fragment shaders](https://docs.flutter.dev/development/ui/advanced/shaders) in the Flutter docs. It’s a really fascinating approach to coding! + +Initially, just to show one possibility, I wrote a simple GLSL shader to act like a filter on one of my `WonkyChar` widgets (described above), to create the following effect: + +<DashImage figure src="images/0-N5jTlkJHFmEeb0e.webp" alt="*WonkyChar widget before and after applying the shader.*" caption="*WonkyChar widget before and after applying the shader.*" /> + + +## Project description + +These raw components — typography, variable font support, and shaders — were an interesting start, but they needed to actually come together in a coherent project. As a self-assignment to learn more about coding in Flutter and the Flutter community, I made a simple tile-based puzzle game, Type Jam, inspired by the [2022 Flutter Puzzle Hack](https://flutterhack.devpost.com/) competition. Brainstorming around these ideas in context of the puzzle hack prompt, I came up with the following concept: You, the player, have to help a beleaguered font company put a glitchy font file back together by solving a series of tile-based puzzles. + +Each screen of the app features a puzzle consisting of scrambled pieces of a glitchy letter, surrounded by whole letters animating according to a variable font axis, like weight or width. The glitchy effect on the puzzle tiles is created by a different shader on each page, causing the tile pieces to look wavy, or fractured, etc. And, in a switch from the usual tile puzzle mechanics, I decided to have players solve puzzles by rotating tiles rather than sliding them, so that the entire letter could be viewed upon completion, with no empty slots. Looking back, this might have made the puzzles a little too easy, but maybe I can come up with more challenges for version 2.0! + +<DashImage figure src="images/090hPGzJLj7qBZp7t.webp" alt="*Variable fonts plus shader effects (on the tiles pieces) applied in the puzzle game app. Letters on this screen animate on the “optical size” axis.*" caption="*Variable fonts plus shader effects (on the tiles pieces) applied in the puzzle game app. Letters on this screen animate on the “optical size” axis.*" /> + + +If you’d like to see how it all came together, check out the [`varfont_shader_puzzle`](https://github.com/flutter/samples/tree/main/experimental/varfont_shader_puzzle) example for the Type Jam app in the [flutter/samples repo](https://github.com/flutter/samples), in the “experimental” folder. As I mentioned before, I’m still pretty new to Flutter, and I have just a basic working knowledge of shaders, so I’m looking forward to improving it going forward! For now, please think of it as a quick demo, rather than as a template. + +## Conclusion + +As a design-focused UX engineer, it was incredibly satisfying to combine my design and technical interests to create playful typography, and allow others to play with the letters, too. Plus, I saw for myself Flutter’s potential as a creative tool that can go beyond the typical patterns for app design. I had a great time making this project, and hope it inspires other designer–coders to try out their weird and fun ideas, and bring their designs to life as real apps! \ No newline at end of file diff --git a/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1-8wNvN-zrrEQlIZZKT5s_A.webp b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1-8wNvN-zrrEQlIZZKT5s_A.webp new file mode 100644 index 0000000000..fe9cba953c Binary files /dev/null and b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1-8wNvN-zrrEQlIZZKT5s_A.webp differ diff --git a/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1FcYP_acRHIIHKbGaAyM-9w.webp b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1FcYP_acRHIIHKbGaAyM-9w.webp new file mode 100644 index 0000000000..474dbf29e6 Binary files /dev/null and b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1FcYP_acRHIIHKbGaAyM-9w.webp differ diff --git a/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1MBoVBDD8a68IiUpcaD3CjQ.webp b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1MBoVBDD8a68IiUpcaD3CjQ.webp new file mode 100644 index 0000000000..5bf82c8d3c Binary files /dev/null and b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1MBoVBDD8a68IiUpcaD3CjQ.webp differ diff --git a/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1MKXFenx4GEY1_JxHsdaSjQ.webp b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1MKXFenx4GEY1_JxHsdaSjQ.webp new file mode 100644 index 0000000000..d17fe319da Binary files /dev/null and b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1MKXFenx4GEY1_JxHsdaSjQ.webp differ diff --git a/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1TlX5df1vv352ypRRfveEAA.webp b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1TlX5df1vv352ypRRfveEAA.webp new file mode 100644 index 0000000000..a172b4d593 Binary files /dev/null and b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1TlX5df1vv352ypRRfveEAA.webp differ diff --git a/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1YxI3oc-2oOSmCB0U5_Q2HA.webp b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1YxI3oc-2oOSmCB0U5_Q2HA.webp new file mode 100644 index 0000000000..c440b40d25 Binary files /dev/null and b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1YxI3oc-2oOSmCB0U5_Q2HA.webp differ diff --git a/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1ZVLNiQC-KCmz3e7t_w3LQg.webp b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1ZVLNiQC-KCmz3e7t_w3LQg.webp new file mode 100644 index 0000000000..00ab45acaf Binary files /dev/null and b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1ZVLNiQC-KCmz3e7t_w3LQg.webp differ diff --git a/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1bEv-aW0gogJaxlpTDysf3Q.webp b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1bEv-aW0gogJaxlpTDysf3Q.webp new file mode 100644 index 0000000000..47548ae2ba Binary files /dev/null and b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1bEv-aW0gogJaxlpTDysf3Q.webp differ diff --git a/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1iDRQWjq4Y_mWd4Diyd67jQ.webp b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1iDRQWjq4Y_mWd4Diyd67jQ.webp new file mode 100644 index 0000000000..6c783af2f9 Binary files /dev/null and b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1iDRQWjq4Y_mWd4Diyd67jQ.webp differ diff --git a/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1iok8mEWRxvbqaZa_8dF9qQ.webp b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1iok8mEWRxvbqaZa_8dF9qQ.webp new file mode 100644 index 0000000000..dc506e6678 Binary files /dev/null and b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1iok8mEWRxvbqaZa_8dF9qQ.webp differ diff --git a/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1jSbyYB6C2wBoP5x_3FNGtA.webp b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1jSbyYB6C2wBoP5x_3FNGtA.webp new file mode 100644 index 0000000000..b228979244 Binary files /dev/null and b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1jSbyYB6C2wBoP5x_3FNGtA.webp differ diff --git a/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1jc6WYuRTFSJVslRi70YBHw.webp b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1jc6WYuRTFSJVslRi70YBHw.webp new file mode 100644 index 0000000000..28345def31 Binary files /dev/null and b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1jc6WYuRTFSJVslRi70YBHw.webp differ diff --git a/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1kS5PXfU-kAN-NgwijJFQ7w.webp b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1kS5PXfU-kAN-NgwijJFQ7w.webp new file mode 100644 index 0000000000..36941f8d76 Binary files /dev/null and b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1kS5PXfU-kAN-NgwijJFQ7w.webp differ diff --git a/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1ntk5Tcf1yuOhmHDlE8Zjig.webp b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1ntk5Tcf1yuOhmHDlE8Zjig.webp new file mode 100644 index 0000000000..0b42cb2fc7 Binary files /dev/null and b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1ntk5Tcf1yuOhmHDlE8Zjig.webp differ diff --git a/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1tNI6e9KnMvxqSyJGqGA-gQ.webp b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1tNI6e9KnMvxqSyJGqGA-gQ.webp new file mode 100644 index 0000000000..27b4d79237 Binary files /dev/null and b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1tNI6e9KnMvxqSyJGqGA-gQ.webp differ diff --git a/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1wA1fIv94nkn0LnY5OJ3IXg.webp b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1wA1fIv94nkn0LnY5OJ3IXg.webp new file mode 100644 index 0000000000..013aa2fc99 Binary files /dev/null and b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/images/1wA1fIv94nkn0LnY5OJ3IXg.webp differ diff --git a/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/index.md b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/index.md new file mode 100644 index 0000000000..192e5906a0 --- /dev/null +++ b/sites/www/content/blog/profiling-flutter-applications-using-the-timeline/index.md @@ -0,0 +1,209 @@ +--- +title: "Profiling Flutter Applications Using the Timeline" +description: "Using the Timeline allows you to find and address specific performance issues in your application. It is also a great tool to identify the…" +publishDate: 2019-03-01 +author: chinmaygarde +image: images/1MBoVBDD8a68IiUpcaD3CjQ.webp +category: tutorial +layout: blog +--- + +*Using the Timeline allows you to find and address specific performance issues in your application. It is also a great tool to identify the relative performance costs of all the features provided by Flutter, and allows you to make more informed decisions about where you might want to avoid certain features or identify headroom to use effects that might make your application stand out.* + +<DashImage figure src="images/1MBoVBDD8a68IiUpcaD3CjQ.webp" alt="The Timeline" caption="The Timeline" /> + + +One of the more useful profiling tools provided by Flutter out of the box is the ability to record traces to the Dart Timeline. The timeline allows you to ask and answer specific questions about why your application might be janking. As someone who is often assigned to look at performance issues in unfamiliar codebases, profiling using the timeline happily requires minimal understanding of the codebase of the application. Because of this, the timeline is often the first and most useful tool I personally use to diagnose and fix performance issues. For performance bottlenecks in Flutter itself, [filing an issue](https://github.com/flutter/flutter/issues) that includes reduced test cases and a Timeline trace make it easy to identify, cross reference, and prioritize specific tasks that improve performance in the Flutter Engine and framework. I must admit that for similarly prioritized issues, I’ll pick the issue with an attached timeline trace to work on first. + +## What is the Timeline + +The Timeline is a [ring buffer](https://en.wikipedia.org/wiki/Circular_buffer) of events that are recorded by application code as it goes about its operation. The type of events to be recorded and their recording frequency is determined by what the authors of the subsystem that emitted the event have determined to be of possible relevance to performance. + +To work with the Timeline, you need the following: + +* An ability to start and stop recording timeline events. + +* An ability to export the timeline events into a portable format and view them offline. + +* Code that emits Timeline events. + +## Timeline Trace Event Format + +The events logged in the ring buffer are extremely lightweight. To actually view these events in a diagnosable form, you must export it to a suitably portable format. The [Trace Event Format](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit) is used by Flutter to export these timeline events for viewing in a dedicated trace viewer. This is the same format and viewer developed in the [Catapult](https://github.com/catapult-project/catapult) family of performance profile gathering, display and analysis tools. + +The Trace Event Format and Viewer and used by a number of other projects. Such projects include [Chromium](http://dev.chromium.org/developers/how-tos/trace-event-profiling-tool) & [Android (via systrace)](https://developer.android.com/studio/command-line/systrace). In fact, it is so useful that Chromium has one built-in. Try navigating to *chrome://tracing* in a Chromium based browser. + +Traces that you share with other developers are JSON files or their tarballs. A neat tip that I didn’t know until recently is that the trace viewer can untar the JSON file tarball for you automatically. + +## Some Background on How Flutter Renders Frames + +Before we attempt to identify potential performance issues, we need to have some understanding of what a healthy Flutter application looks like. This section is a crash course in how Flutter renders its frames. + +### The Threads + +When a Flutter application is launched, it starts (or picks from a pool) three more threads. These threads sometimes have overlapping concerns, but broadly, they are referred to as the UI thread, the GPU thread and the IO thread. It is important to note the the UI thread is not the same as the platform’s main thread. Some platforms also called their main threads their UI thread. The UI thread used by Flutter is different from this thread. Descriptively, Flutter calls the platform’s main thread the “Platform thread”. + +<DashImage figure src="images/1ZVLNiQC-KCmz3e7t_w3LQg.webp" alt="Threading in the Flutter Engine" caption="Threading in the Flutter Engine" /> + + +The UI thread is the thread on which all Dart code executing on behalf of the framework and your application is run. Unless your application or the framework (via methods like [foundation::compute](https://docs.flutter.io/flutter/foundation/compute.html)) launches its own isolates (via methods like [Isolate.spawn](https://docs.flutter.io/flutter/dart-isolate/Isolate/spawn.html)), Dart code will never run on any other thread. Even in cases of secondary isolate launches, Dart code will never run on either the GPU or IO threads. + +The Platform thread is where all code that relies on plugins runs. This thread is also where the platform’s native frameworks might service their tasks. Flutter applications interact with their plugins in an asynchronous manner and plugins should not be able to block any of the threads managed by Flutter. + +Apart from the four threads just described, the Dart VM also manages a pool of threads. This thread pool is used to service a variety of functions like waiting on socket for *dart:io*, garbage collection and JIT code generation (only in *debug* mode and Flutter uses AOT in *release* mode). + +### The Frame Pipeline + +<DashImage figure src="images/1jc6WYuRTFSJVslRi70YBHw.webp" alt="Frame Pipeline" caption="Frame Pipeline" /> + + +To generate a single frame, the Flutter engine first arms the vsync latch. A vsync event is an indication to the Flutter engine to start doing work that eventually renders a new frame on the screen. The vsync event generated by the platform takes into account the refresh rate of the display (among other factors). + +The vsync event wakes up the UI thread. This is where the Dart code for the Flutter application and the framework runs. The result of all the operations on the UI thread is a rendering backend (OpenGL, Vulkan or Software) agnostic description of the scene to render to the screen. This description is called the layer tree. + +As soon as the layer tree is created, the GPU thread is woken up. This thread begins the transformation of the layer tree into a GPU command buffer. This command buffer is then submitted to the GPU on the same thread. + +For sufficiently complex scenes, the UI thread could be generating the next frame concurrently as the GPU thread is consuming the layer tree for the previous one. The engine refers to the work done on the UI and GPU threads as belonging to a pipeline item. The pipeline depth is the number of frame workloads the engine is working on at any given time. The pipeline depth may vary. + +### What Causes Jank + +Give this mode of operation, jank can be perceived in a Flutter application if either one of the following conditions are met: + +* Either the UI or GPU thread component of a pipeline item exceeds the frame budget (typically 16.67 ms for a 60Hz display refresh rate). + +* The engine changes the pipeline depth. + +* The vsync events from the platform are emitted or latched onto at an inconsistent rate. + +One notable exception to this list is a scenario where the engine selectively ignores vsync events at a consistent rate. For example, on a 60Hz display, if the engine only starts working on a pipeline item on every other vsync pulse, the Flutter application will render at a consistent 30Hz. + +<DashImage figure src="images/1iDRQWjq4Y_mWd4Diyd67jQ.webp" alt="Janky Frame (due to shader compilation)" caption="Janky Frame (due to shader compilation)" /> + + +## How to Collect & Read Timeline Traces + +Armed with this knowledge of how the Flutter engine operates as well as having a cursory understanding of the conditions that cause jank, we are ready to collect traces ourselves. + +The trace collection overhead is fairly low but it is definitely not free. For this reason, the Flutter engine gathers traces in only the [debug](https://flutter.dev/docs/testing/build-modes#debug) or [profile](https://flutter.dev/docs/testing/build-modes#profile) modes. The profile mode is most similar to the performance your users can expect when running your application. This mode AOT compiles your Dart code and otherwise behaves just like [release](https://flutter.dev/docs/testing/build-modes#release) mode. However, it also enables the timeline as well as other tracing tools available via the Dart observatory. + +The time it takes to create a profile mode build is also about the same as the release mode. Don’t let this discourage you from using profile mode, however. I usually like to add traces to the Timeline in debug mode (with all the benefits hot reloading brings to the table). Then, when I am reasonably sure that my traces will collect the information I need, I perform one build in profile mode to gather valid timing information. + +To collect traces, you need to open the Dart Observatory for your Flutter application. The Observatory is a web application served by all Flutter applications running on the device in either debug or profile mode. The web application is launched on the device and is provided at a randomly available port. The Flutter tools do the work of forwarding this port to your host. +> **Note**: Observatory is being replaced by Dart DevTools. This browser-based tool is still in development but is available to preview. For installation and getting started instructions, see the [DevTools’ docs](https://flutter.github.io/devtools/) and, in particular, [using the timeline view](https://flutter.github.io/devtools/timeline). + +### Observatory from the Command Line + +* Launch the application using ***flutter run — profile*** (or ***debug*** if you are prototyping your traces) + +* The tools should forward the observatory port to your host and display a line like so: ***An Observatory debugger and profiler on iPhone X is available at: http://127.0.0.1:60551/*** + +* Launch that URL in your web browser + +### Observatory from the IDE + +* Launch the Flutter Application using the Play button or shortcut. + +* In the Run panel, click the stopwatch icon to launch the observatory for that application in your web browser. + +<DashImage figure src="images/1wA1fIv94nkn0LnY5OJ3IXg.webp" alt="Observatory from the IDE" caption="Observatory from the IDE" /> + + +## Navigate to the Timeline in the Observatory + +Either click on the timeline link on the main observatory page or navigate to the timeline directly by opening **http://127.0.0.1:61102/#/timeline** (replace the # with the actual port number). + +If you always want to forward the observatory to the same port on the host, specify the ***— observatory-port=<port>*** command line option. That way, you can just reload your web browser after relaunching the application on the device. I do this to avoid looking up the new observatory port. Enter **flutter help run** at the command line for more details. + +<DashImage figure src="images/1ntk5Tcf1yuOhmHDlE8Zjig.webp" alt="The Observatory Homepage" caption="The Observatory Homepage" /> + + +You will now see an empty timeline. Trace are assigned categories, and, by default, no categories are enabled. + +<DashImage figure src="images/1TlX5df1vv352ypRRfveEAA.webp" alt="An Empty Timeline" caption="An Empty Timeline" /> + + +## Collecting a Trace + +Enable tracing on all trace categories by clicking on ***All*** then click the ***clear*** button to start tracing on the device. Notice that the “Recorded Streams” are all enabled. The timeline is still empty but the device has started gathering traces. Make sure to interact with the application in a way that adds traces to the Timeline. Usually, rendering a few frames works. + +<DashImage figure src="images/1kS5PXfU-kAN-NgwijJFQ7w.webp" alt="Timeline with Streams Enabled" caption="Timeline with Streams Enabled" /> + + +When you are done tracing and want to view the trace, click the ***Refresh*** button to have the Observatory pull the current trace buffer from the device. + +<DashImage figure src="images/1tNI6e9KnMvxqSyJGqGA-gQ.webp" alt="A Freshly Collected Trace (enable Flow & VSync events)" caption="A Freshly Collected Trace (enable Flow & VSync events)" /> + + +You can quickly navigate the trace using the mouse and ***W***, ***A***, ***S***, ***D*** keys. It is also a good idea to enable the ***Flow events*** and ***Highlight VSync*** options in the ***View Options*** dropdown. This allows you to view traces within the context of frames as they are being rendered on the screen. + +Navigating the trace using the mouse depends on what navigation mode is selected. I usually have the ‘selection’ tool enabled from the panel. I use this to select specific trace elements while using the keyboard keys to pan. However, can switch between the mouse mode by clicking the appropriate panel item or pressing one of ***1*** for selection , ***2*** for pan, ***3*** for zoom or ***4*** for timing. + +<DashImage figure src="images/1jSbyYB6C2wBoP5x_3FNGtA.webp" alt="The Selection Pane (Easy to miss, so just use the shortcuts)" caption="The Selection Pane (Easy to miss, so just use the shortcuts)" /> + + +It is also a good idea to press ***?*** on your keyboard. This brings up a help dialog of supported shortcuts. + +## Saving & Sharing Traces + +Clicking the ***save*** button makes the browser download a JSON file containing the trace. You can share that trace in bug reports or email. To view a shared trace, navigate to ***about://tracing*** in Chrome and load the previously saved trace file. + +The trace viewer in Chrome at ***about://tracing*** works with compressed JSON files as well. I find these to be easier to share and work with. + +## Elements of a Trace + +### Duration Events + +By far the most common type of trace events used in the engine are duration events. Such events allow you to annotate a block of code in your trace. These are extremely simple to add as they require no identifiers. In Dart, you can use the [***dart:developer*** package’s ***Timeline***](https://docs.flutter.io/flutter/dart-developer/Timeline-class.html) class to add traces yourself. The Flutter engine & framework already adds duration events to workloads it considers important. You should feel free to do the same. Clicking on a specific duration gives you a summary of the time spent on that event. + +<DashImage figure src="images/1YxI3oc-2oOSmCB0U5_Q2HA.webp" alt="Duration Events & Summary" caption="Duration Events & Summary" /> + + +Look for duration events on the UI or GPU threads that are significant chunks of the frame interval. The frame intervals can be highlighted by enabling the ***Highlight Vsync*** button described earlier or just pressing the ***v*** key. + +If you see a particularly large duration event, the next step is to highlight which part of code is contributing to that chunk being as large as it is. Identifying such chunks is significantly easier when using the sampling profiler described below. But, if you have some understanding of the codebase in question, you can also speculatively add traces to your code. While firm instrumentation numbers should only be gathered in ***profile*** mode, I like to work in ***debug*** mode with hot reload to speculatively add traces to see if I am getting closer to isolating the bottleneck. + +### The Event Summary + +Clicking on an event displays the event summary in a pane at the bottom. The ***Events*** section of the summary is especially useful because it attempts to connect all logically related duration events. These relationships are inferred using flow events described below. The Flutter framework and engine already add flow events for all frame related workloads. In this way, it is easier for you to isolate all the work (across multiple threads) related to a specific frame. When you click the link for the related flow, the trace viewer selects and highlights all the connected flows. + +In the example below, selecting all related traces and sorting the list by ***Self Time*** shows that the ***PhysicalShapeLayer::Paint*** trace is the dominant trace. You can tell that this trace is on the GPU thread because mousing over the same in the summary highlights the same. + +<DashImage figure src="images/1iok8mEWRxvbqaZa_8dF9qQ.webp" alt="The Event Summary" caption="The Event Summary" /> + + +Once such dominant traces are identified, I usually know which parts of the code to dig down deeper into. The summary is also useful because multiple occurrences of small events with extremely small durations are easy to miss visually in the trace. But such traces are immediately highlighted in the summary. + +### Repeating Events + +Sometimes, instead of a single event, you want to characterize the performance of repeating events that generate duration traces. This can be done by drag selecting a section of the timeline containing such events. + +<DashImage figure src="images/1bEv-aW0gogJaxlpTDysf3Q.webp" alt="Analyzing Repeating Events" caption="Analyzing Repeating Events" /> + +> The trace viewer is called [Catapult](https://www.google.com/url?q=https://github.com/catapult-project/catapult&sa=D&ust=1551398816863000) and there may be different versions of Catapult embedded in Chrome at ***about://tracing*** and the Observatory. I am using the latest version of Chrome for this as it has more up-to-date trace viewer features. You can also use Catapult directly at HEAD on GitHub. If you want to view a trace gathered in Observatory in a newer version of Catapult, save the trace as JSON and load the same in a different version of Catapult. The JSON format is stable. + +Once you select a group of traces, you should see a summary of all the traces that are repeating within that selection. Selecting a trace (I am using the latest version of the Catapult trace viewer in the following example), should display useful information like the average time per slice, standard deviation, repeat counts, etc.. + +<DashImage figure src="images/1FcYP_acRHIIHKbGaAyM-9w.webp" alt="Repeating Event Summary" caption="Repeating Event Summary" /> + + +This should give you a better idea about improvements you make to the codebase that are reflected in smaller duration events in repeating events (like frames). Also, once you identify outliers, you can more easily select the same and analyze the traces around the same to explain the deviation. + +### Flow Events + +Flow events are used to logically connect duration events (possibly on multiple threads). In the trace viewer, they show up as arrows. By default, Flow events tend to make the trace view quite messy and are disabled. To enable the same, check the “Flow events” box in the “View Options”. Flow events must originate within a duration event. Using the [Flow class in the ***dart:developer***](https://docs.flutter.io/flutter/dart-developer/Flow-class.html) package does this for you automatically. The Flutter engine also adds an implicit flow called “PipelineItem” to all frame related workloads. All duration events connected with a flow count as “related” in the events summary described above. The descendants of connected events are also counted. + +In the following example, the Flutter engine is producing the next frame on the UI thread before the previous frame has been rendered by the GPU thread. Without flows, it would have been very difficult to correlate duration events with specific frame workloads. + +<DashImage figure src="images/1-8wNvN-zrrEQlIZZKT5s_A.webp" alt="Flow Events for Pipeline Items" caption="Flow Events for Pipeline Items" /> + + +### The Sampling Profiler + +Often, it can be hard to figure out where to begin when adding traces. Whenever the engine wakes up a thread to do work, it adds an implicit duration trace called ***MessageLoop::RunExpiredTasks***. However, there might be no more traces nested within the same. In such case, the sampling profiler comes in extremely handy. The Dart VM collects the backtrace of currently code at a specific frequency. These samples show up as instant events on the UI thread whenever any Dart code is being run. + +<DashImage figure src="images/1MKXFenx4GEY1_JxHsdaSjQ.webp" alt="The Sampling Profiler" caption="The Sampling Profiler" /> + + +The samples are easy to miss but extremely handy. Selecting a sample displays a backtrace of the Dart and native stack at that point in time. If you are lost and don’t know where to begin adding traces, just keep selecting a sample till you identify code that looks familiar. + +And, that’s a short overview of some of the ways in which you can use the timeline effectively to instrument your application. Happy tracing. \ No newline at end of file diff --git a/sites/www/content/blog/progress-of-the-flutter-package-ecosystem/images/0BhBIcosj4JSij9xk.webp b/sites/www/content/blog/progress-of-the-flutter-package-ecosystem/images/0BhBIcosj4JSij9xk.webp new file mode 100644 index 0000000000..c8c113e2be Binary files /dev/null and b/sites/www/content/blog/progress-of-the-flutter-package-ecosystem/images/0BhBIcosj4JSij9xk.webp differ diff --git a/sites/www/content/blog/progress-of-the-flutter-package-ecosystem/images/0NzfQsOUhVaeUcGB8.webp b/sites/www/content/blog/progress-of-the-flutter-package-ecosystem/images/0NzfQsOUhVaeUcGB8.webp new file mode 100644 index 0000000000..5f0e8e577c Binary files /dev/null and b/sites/www/content/blog/progress-of-the-flutter-package-ecosystem/images/0NzfQsOUhVaeUcGB8.webp differ diff --git a/sites/www/content/blog/progress-of-the-flutter-package-ecosystem/images/0U2blC-2k01FIuCsZ.webp b/sites/www/content/blog/progress-of-the-flutter-package-ecosystem/images/0U2blC-2k01FIuCsZ.webp new file mode 100644 index 0000000000..f96d1dfa61 Binary files /dev/null and b/sites/www/content/blog/progress-of-the-flutter-package-ecosystem/images/0U2blC-2k01FIuCsZ.webp differ diff --git a/sites/www/content/blog/progress-of-the-flutter-package-ecosystem/images/0gI42oCiw6spGZfOy.webp b/sites/www/content/blog/progress-of-the-flutter-package-ecosystem/images/0gI42oCiw6spGZfOy.webp new file mode 100644 index 0000000000..2569ed9a48 Binary files /dev/null and b/sites/www/content/blog/progress-of-the-flutter-package-ecosystem/images/0gI42oCiw6spGZfOy.webp differ diff --git a/sites/www/content/blog/progress-of-the-flutter-package-ecosystem/images/0tkq_040X0xa_Noms.webp b/sites/www/content/blog/progress-of-the-flutter-package-ecosystem/images/0tkq_040X0xa_Noms.webp new file mode 100644 index 0000000000..ce6e543ae2 Binary files /dev/null and b/sites/www/content/blog/progress-of-the-flutter-package-ecosystem/images/0tkq_040X0xa_Noms.webp differ diff --git a/sites/www/content/blog/progress-of-the-flutter-package-ecosystem/index.md b/sites/www/content/blog/progress-of-the-flutter-package-ecosystem/index.md new file mode 100644 index 0000000000..fcf63db6d5 --- /dev/null +++ b/sites/www/content/blog/progress-of-the-flutter-package-ecosystem/index.md @@ -0,0 +1,84 @@ +--- +title: "Progress of the Flutter Package Ecosystem" +description: "Continued growth, new Flutter Favorites, Ecosystem Virtual Summit and things to know" +publishDate: 2024-01-22 +author: anderdobo +image: images/0tkq_040X0xa_Noms.webp +category: announcements +layout: blog +--- + +## Progress of the Flutter and Dart Package Ecosystem + +The Flutter and Dart package ecosystem remains a key enabler for developers to build beautiful, performant apps for any screen from a single codebase. The ecosystem grew 26% in 2023 from 38,000 packages in January to 48,000 at the end of December. + +<DashImage figure src="images/0tkq_040X0xa_Noms.webp" /> + + +Pub.dev now has more than 700,000 monthly active users as of January 2024. The Flutter team remains keen and committed to supporting this growth into the future, and enabling developers to build with and contribute to Flutter and Dart. In this update, we’ll take a look at the newest Flutter Favorites, results of the Package Ecosystem Virtual Summit and share some notable updates and things to know. + +## New Flutter Favorites + +<DashImage figure src="images/0gI42oCiw6spGZfOy.webp" /> + + +The [Flutter Favorites](https://docs.flutter.dev/packages-and-plugins/favorites) program recognizes and helps developers discover the highest quality packages to consider using in their apps. We’re pleased to announce seven new Flutter Favorite packages that have demonstrated exceptional quality, popularity, and community engagement, making them invaluable tools for Flutter developers. Let’s dive into each: + +1. **[flame](https://pub.dev/packages/flame):** A high-performance 2D game engine for Flutter. Its intuitive API and rich feature set make it an ideal choice for creating visually stunning and engaging games. Check out [this codelab](https://codelabs.developers.google.com/codelabs/flutter-flame-game) to try your hand at building a game with flame. + +1. **[flutter_animate](https://pub.dev/packages/flutter_animate):** Bring your UI to life with this a powerful animation library that simplifies complex animations and makes them accessible to all Flutter developers. Its declarative syntax and extensive documentation make it a breeze to create smooth and expressive animations. + +1. **[riverpod](https://pub.dev/packages/riverpod):** An elegant package that offers a powerful and intuitive approach to managing application state. Its streamlined API, performance, scalability, and testability make it a compelling choice for modern Flutter apps. + +1. **[video_player](https://pub.dev/packages/video_player):** Essential for anyone looking to integrate video playback in their Flutter applications. It provides a widget to display video content. It supports a wide range of formats and sources, including network asset and file-based videos. This makes it a versatile tool for building multimedia-rich Flutter apps. + +1. **[macos_ui](https://pub.dev/packages/macos_ui):** For developers targeting macOS, this package enables creation of applications with a design that feels right at home on that platform. It provides an extensive collection of widgets and components that are styled according to the macOS design language, ensuring that your Flutter app not only runs well on macOS, but also looks and feels native. + +1. **[fpdart](https://pub.dev/packages/fpdart):** This package enables functional programming in Dart. It’s great for implementing business logic, for instance, where functional programming paradigms like immutability, pure functions, and higher-order functions, as well as fpdart’s use of Dart’s type system, helps in building more maintainable and predictable code. + +1. **[flutter_rust_bridge](https://pub.dev/packages/flutter_rust_bridge):** For developers seeking to leverage the best of Rust and Flutter in their application, flutter_rust_bridge provides a seamless bridge between the two worlds. It enables native Rust code to interact with Flutter seamlessly, unlocking the potential of Rust’s performance and memory safety in Flutter apps. + +## Sunsetting the Happy Paths program + +We decided to sunset the Happy Paths program to enable a more dedicated focus on Flutter Favorites. The vision of Happy Paths recommendations was to help you to make informed decisions on finding and using packages to add functionality to your app. We are fortunate to have community initiatives such as [Flutter Gems](https://fluttergems.dev/) that are comprehensive resources for navigating well categorized package options. As we focus on the Flutter Favorites program, we will continue to evolve it with input and feedback from the Flutter and Dart community. + +## Package Ecosystem Virtual Summit + +<DashImage figure src="images/0NzfQsOUhVaeUcGB8.webp" /> + + +At the end of August 2023, we held a first-time [virtual summit](https://rsvp.withgoogle.com/events/flutter-package-ecosystem-summit-2023) for the Flutter and Dart package ecosystem, attended by more than 50 non-Googler and Googler contributors to [pub.dev](https://pub.dev/). We started with a relatively small invitee list to fit the unconference-style format, and to learn from this first-time event before figuring out what it might look like in the future. The goal was to bring contributors together in unconference-style discussions to plan, educate, learn, and share amongst the community. We had three discussion sessions, each on topics that were voted on by registered attendees in the weeks leading up to the summit. The three discussion topics were 1) Building high quality packages — best practices, and challenges, 2) Maintaining packages long term — sustainable models, and 3) Flutter and Dart DevTools Extensions. ****Respondents to the post-event survey gave us insightful feedback that we’ll incorporate in future event planning. Thank you! Overall, we consider this first summit a success. Going forward, we’re keen to partner with the community on similar standalone events, or sessions focused on the Flutter and Dart ecosystem, set within more general events. + +## Updates to the Pigeon package + +The [Pigeon package](https://pub.dev/packages/pigeon) is a code generation tool that streamlines setting up the communication between your Flutter app and platform-specific code. This makes Pigeon useful both 1) when writing custom integrations directly between a Flutter app and platform-native APIs, such as in an [add-to-app](https://docs.flutter.dev/add-to-app) scenario, and 2) when writing a [Flutter plugin](https://docs.flutter.dev/packages-and-plugins/developing-packages#types) to provide a Dart API surface for platform-native APIs. It’s maintained by the Flutter Team who has made the following notable improvements to the package over this year: + +* Added support for Swift, Kotlin and C++ (C++ unlocked Windows support). + +* Null safety is now enforced. + +* Expanded support for primitive data types support. For example, `enums` were added as a supported type. + +* Added nullable parameters. + +* Added error handling on host and Flutter APIs. + +* Improved the ergonomics of the tools to make them easier and more intuitive to use. For example, we added support for default parameters and named parameters. + +There are a lot more developments between v5.0.0 in January and v15.0.2 in December than we can list here, so check out all the changes in the [change log](https://pub.dev/packages/pigeon/changelog)! + +## Packages in DartPad + +[DartPad](https://dartpad.dev/) supports a fixed set of packages that you can view by clicking the info icon in the bottom, right-hand corner of the screen. The Flutter and Dart team at Google reviews and prioritizes package requests on an ongoing basis. If you’d like a package to be added to DartPad, add your thumbs up to an [existing package suggestion](https://github.com/dart-lang/dart-pad/issues?q=is%3Aissue+is%3Aopen+label%3Asuggested-package+sort%3Areactions-%2B1-desc), if there is one, or [open a new issue](https://github.com/dart-lang/dart-pad/issues/new?assignees=&labels=&projects=&template=everything-else.md&title=) with your suggestion. + +<DashImage figure src="images/0U2blC-2k01FIuCsZ.webp" /> + + +## Proposal for canonical topics on pub.dev + +In 2023 we launched the ability for package authors to tag their package with 1–5 free text topics in the pubspec file. The goal was to improve discovery of packages by potential users by adding a form of package categorization. We’ve seen a healthy uptake of the feature with many packages tagged. We’re exploring a proposal to improve the feature by merging topics that are effectively the same (For example, `widget` and `widgets`). We invite the community to share feedback or contribute PRs to this [canonicalize topics issue](https://github.com/dart-lang/pub-dev/issues/7263). + +<DashImage figure src="images/0BhBIcosj4JSij9xk.webp" /> + + +That’s it for now! To engage with the amazing community of package authors, check out the [#package-authors](https://discord.com/channels/608014603317936148/1014208569706561567) Discord channel (you first need to join the [Flutter Discord server](https://github.com/flutter/flutter/wiki/Chat)). \ No newline at end of file diff --git a/sites/www/content/blog/prospera-using-flutter-to-bring-ai-to-the-frontline-of-sales/images/1rV1FMqYeyrHsOvtSUIOnOA.webp b/sites/www/content/blog/prospera-using-flutter-to-bring-ai-to-the-frontline-of-sales/images/1rV1FMqYeyrHsOvtSUIOnOA.webp new file mode 100644 index 0000000000..265ffae9cb Binary files /dev/null and b/sites/www/content/blog/prospera-using-flutter-to-bring-ai-to-the-frontline-of-sales/images/1rV1FMqYeyrHsOvtSUIOnOA.webp differ diff --git a/sites/www/content/blog/prospera-using-flutter-to-bring-ai-to-the-frontline-of-sales/index.md b/sites/www/content/blog/prospera-using-flutter-to-bring-ai-to-the-frontline-of-sales/index.md new file mode 100644 index 0000000000..dc3bd099fc --- /dev/null +++ b/sites/www/content/blog/prospera-using-flutter-to-bring-ai-to-the-frontline-of-sales/index.md @@ -0,0 +1,35 @@ +--- +title: "Prospera: using Flutter to bring AI to the frontline of sales" +description: "Developers around the world are using Google’s Gemini models to tackle real-world challenges, and one of the most exciting applications…" +publishDate: 2024-11-21 +author: tvolkert +image: images/1rV1FMqYeyrHsOvtSUIOnOA.webp +category: spotlight +layout: blog +--- + +<DashImage figure src="images/1rV1FMqYeyrHsOvtSUIOnOA.webp" /> + + +Developers around the world are using Google’s Gemini models to tackle real-world challenges, and one of the most exciting applications we’ve seen is in the field of sales. Meet Prospera, winner of both *best Flutter app* and *most Useful app* in our recent [Gemini API Developer Competition](http://ai.google.dev/competition)! Created by Leon Kukuk and Max Hasenohr from Germany, Prospera is an AI-powered sales coach that provides real-time guidance and feedback during and after calls. This innovative Flutter app showcases the power of Gemini to transform how sales professionals learn and improve their skills. + +<YoutubeEmbed id="ZTFKbhmGcUo" title="Prospera Gemini Demo" fullwidth="true"/> + + +## Gemini API at the core: real-time analysis and multimodal magic + +Prospera leverages Gemini’s multimodal capabilities in a truly unique way. By feeding snippets of audio conversations directly into the Python SDK for the Gemini API, the app provides real-time feedback from the perspective of a seasoned sales coach. Imagine having an AI co-pilot whispering valuable insights and suggestions during your calls — that’s the power of Prospera. + +But how did they achieve this seamless integration? Leon, the developer of Prospera, cleverly used Dart FFI (Foreign Function Interface) and the ffigen package to create Dart bindings for the PJSIP C library, enabling seamless phone call integration in Flutter and allowing these calls to be processed through the Gemini API. This allowed them to process audio data in real-time and deliver instant feedback to the sales representative. + +**“We were impressed by Gemini’s nuanced understanding of speech, thanks to its multimodal audio capabilities as well as its ease of implementation. Flutter enabled us to develop cross-platform apps at an incredible speed, while offering powerful opportunities to run native code through FFI and platform channels.” — Leon** + +## A Flutter app for all platforms + +Built with Flutter, Prospera runs seamlessly across various desktop platforms from a single codebase. This not only streamlines development but also opens doors for future expansion to mobile devices, making the app accessible to an even wider audience. The developer also paid close attention to user experience, crafting a beautifully designed app with Material Design widgets and ensuring full accessibility. + +## Beyond sales: a glimpse into the future of communication + +By combining the power of Gemini with the versatility of Flutter, Leon and Max have created an app with the potential to revolutionize how we learn, interact, and excel in our professional lives. Prospera is more than just a sales coach; it’s a glimpse into the future of how AI can enhance human communication and professional development. While Prospera focuses on sales coaching, its underlying technology and approach have broader applications. The ability to analyze real-time conversations and provide constructive feedback can be valuable in various communication-driven professions, such as customer service and education. + +**Want to learn more about Dart FFI and how it can empower your Flutter applications? Check out the [ffigen](https://pub.dev/packages/ffigen) package on pub.dev**. \ No newline at end of file diff --git a/sites/www/content/blog/providing-operating-system-compatibility-on-a-large-scale/images/0XGMajBOXRRgyu3Pa.jpg b/sites/www/content/blog/providing-operating-system-compatibility-on-a-large-scale/images/0XGMajBOXRRgyu3Pa.jpg new file mode 100644 index 0000000000..1e9f81e9d2 Binary files /dev/null and b/sites/www/content/blog/providing-operating-system-compatibility-on-a-large-scale/images/0XGMajBOXRRgyu3Pa.jpg differ diff --git a/sites/www/content/blog/providing-operating-system-compatibility-on-a-large-scale/images/0cn2P4AQs91rSOztB.webp b/sites/www/content/blog/providing-operating-system-compatibility-on-a-large-scale/images/0cn2P4AQs91rSOztB.webp new file mode 100644 index 0000000000..6a84e6a811 Binary files /dev/null and b/sites/www/content/blog/providing-operating-system-compatibility-on-a-large-scale/images/0cn2P4AQs91rSOztB.webp differ diff --git a/sites/www/content/blog/providing-operating-system-compatibility-on-a-large-scale/index.md b/sites/www/content/blog/providing-operating-system-compatibility-on-a-large-scale/index.md new file mode 100644 index 0000000000..e20d6c892f --- /dev/null +++ b/sites/www/content/blog/providing-operating-system-compatibility-on-a-large-scale/index.md @@ -0,0 +1,177 @@ +--- +title: "Providing operating system compatibility on a large scale" +description: "Flutter’s approach to device diversity in software releases" +publishDate: 2021-01-14 +author: kf6gpe +image: images/0XGMajBOXRRgyu3Pa.jpg +category: announcements +layout: blog +--- + +Applications written using Flutter can run on Android, iOS, the web, and desktop operating systems. Because of our investment in supporting multiple platforms, a Flutter application can run on any device with the OS version (or a later version) that the application was written for without needing changes, which broadens Flutter’s cross-platform appeal. + +In this article, Flutter’s core development team (we) want to share why we invest in supporting multiple platforms, how we can keep supporting multiple platforms, and how we make decisions when it comes time to add support for new platforms or drop support for old platforms. + +## Looking ahead: releases of Android and iOS + +Flutter is committed to providing full support for the latest features of Android and iOS. We’re always monitoring the regular guidance from Apple and Google regarding changes in their platform APIs, tool use, and licensing terms. Support for new OS releases is part of our regular product planning, and we attempt to align our releases in such a way that stable releases are always compatible with the latest guidance from both Google and Apple. + +Our commitment to you to support multiple platforms and software releases of Android and iOS, such as Android 11 or iOS 14, along with their minor releases. For major revisions to the target platforms, development of necessary features and changes begin as soon as we learn what’s needed. This typically happens in late spring, after Google announces their plans for an upcoming Android release and when Apple makes their latest announcements at their Worldwide Development Conference (WWDC). After the announcements, we assess the necessary changes to the Flutter codebase, like changes to the engine because of potentially deprecated APIs, how tooling changes affect the flutter tool and the developer experience, and changes in the design language for both platforms. We also assess the scope of work with the expectation that both platforms will be available to customers in the early fall. + +We do this work against the beta releases of Android and iOS, and we make these changes available regularly through our master, dev, and beta channels. The master channel releases are continual. We plan for two to three releases of the dev channel per week (a version of the master channel tested against internal Google apps and test suites). The beta channel usually releases the first week of every month. We track our progress using GitHub issues. For a list of issues, you can search by `platform-ios` or `platform-android`. + +Aligning our stable release schedule with iOS is particularly challenging because often there is very little time between a press announcement and when we get guidance about changes to the OS. We mitigate this challenge by planning one of our releases in the fall, and being prepared to hotfix our current stable release with any blocking bugs on iOS or even on Android should they occur. (This happened with Flutter 1.20 and iOS 14, and we released a [hotfix the same day](https://medium.com/flutter/supporting-ios-14-and-xcode-12-with-flutter-15fe0062e98b) that iOS 14 shipped.) + +Regardless of the work it takes to support a new platform release, our goal is to support new versions of our target platforms without deprecating old ones. + +## Backward compatibility and Flutter’s value proposition + +Flutter is Google’s portable UI toolkit for building beautiful, natively-compiled applications for mobile, web, and desktop from a single codebase. To do this, we want to support the broadest collection of devices we can, not just specific types of platforms. We also want to support the broadest number of platform versions. + +Consider Android: By [statcounter](https://gs.statcounter.com/os-version-market-share/android/mobile-tablet/worldwide), as of June 2020, Android versions in the wild consist of the following: + +<DashImage figure src="images/0cn2P4AQs91rSOztB.webp" /> + + +Looking at the numbers, Android KitKat (Android 4.x) has less than a 2% market share. The predominant share of Android devices (54%) are running Android Pie (Android 9.0) or Android 10. Supporting each version of Android requires an investment. Do you know why we invest in a platform, like KitKat, that has a low market share? + +The answer is simple: raw numbers. With over a billion Android devices in use not running Android Pi or later, we want to ensure that Flutter is as broadly available as possible. We also want to support a diverse, global user base. And, because older versions of Android and iOS are often used by people in developing countries, we want to avoid deprecating support for older versions as long as we can. + +Through work in Flutter’s core engine, framework, and plugins, Flutter provides abstractions for the vagaries of both OS platform and OS versions. As an example, a Material application can run on an older version of iOS or Android without being changed. In fact, developers have released commercial Material applications running on hardware that shipped at the same time that the first version of it was made available, something you can’t do on those devices with the widget set that shipped with those devices. A similar story exists for plugins. Plugins abstract away both platform and version differences, leaving you to focus on what’s important: your application. + +## How Flutter supports so many targets + +Flutter brings two strengths to multiple-target support. First, Flutter relies very little on the underlying OS. Flutter’s engine works directly with the graphics APIs, and plugins provide much of the device-specific implementation for additional features. + +Our continuous integration (CI) system builds Flutter and its tooling for macOS, Linux, and Windows, deploying to a suite of Android and iOS devices (both physical and emulated). Some of this CI is done using a lab run on Google’s premises; other CI tests are run on the [Firebase Test Lab](https://firebase.google.com/docs/test-lab), a cloud-hosted test solution operated by Google for both Google and third-party developers. + +Here’s a picture of one of the racks in our test lab: + +<DashImage figure src="images/0XGMajBOXRRgyu3Pa.jpg" /> + + +We run relatively low-end mobile devices in our device lab, because we do our [performance testing](https://flutter-dashboard.appspot.com/benchmarks.html) there. When testing, we want performance metrics from devices that are representative of what most users have. Doing this gives us worst-case, real-world performance. This philosophy carries over to the devices that the core engineering team uses. Most of the core engineering team isn’t doing Flutter debugging on the latest Google Pixel or Apple iPhone. + +Because we want to test many types of mobile devices, we keep a device library where members of the core engineering team can check out specific devices for testing and debugging. (With COVID-19, we’re making good use of the US Postal Service!) + +Because the lab can only test a handful of device types and versions, how can we be sure that we’re providing the support we claim? Read on to find out. + +## Making general assertions about platform version support + +First and foremost, we test on a variety of platforms with each code commit. + +In our lab today, we’re testing on the following Android platforms: + +* Android API 24 (Android N) + +* Android API 28 (Android P) + +* Android API 29 (Android 10) + +This covers 57% of the market of deployed Android versions; more on how we cover the remaining devices through the use of Firebase Test Lab in a moment. + +Our lab also tests iOS on every commit, on devices running: + +* iOS 9.3.6 + +* iOS 12.4 + +* iOS 12.4.1 + +* iOS 13.1.3 + +* iOS 13.2 + +Finally, for desktop and web, we test every commit on: + +* Chrome 84 + +* Firefox 72.0 + +* Safari on Catalina + +* Edge 1.2.0.0 + +* Windows 10 + +* macOS El Capitan + +* Debian 10 + +Clearly there’s more testing that needs to happen on the Android front. We rely on [Firebase Test Lab](https://firebase.google.com/docs/test-lab) for this, testing the following additional Android and iOS configurations: + +* Android API 19 (Android K) + +* Android API 21 and 22 (Android L) + +* Android API 23 (Android M) + +* Android API 26 and 27 (Android O) + +* Android API 30 (Android 11) + +* iOS 11 + +* iOS 13.0 + +* iOS 13.1 + +* iOS 13.3–13.7 + +* iOS 14 + +**Together this brings our testing of Android and iOS platforms to well over 95% of the currently released versions.** + +For other operating system versions, we provide backward compatibility with **your** support, the Flutter development community. This is especially true of specific device/operating system combinations. Many of you use devices that we don’t have access to: either because you have an operating system version we don’t have, or because the device isn’t available in the US. In some cases, we can mitigate this. For example, we can flash an older Android build on some Google Pixel hardware, buy an older, refurbished iOS device, or run an Android version in an emulator. In many cases, a report from one of you triggered an investigation of a specific platform issue. + +This is especially true with a number of original equipment manufacturers (OEMs) of Android devices. OEMs frequently customize aspects of Android like keyboard support. In the past, we’ve been able to fix a number of text input issues that only occur in certain markets on certain devices thanks to your help. + +We appreciate your help because we can’t possibly find every issue on our own, but we work hard at reducing the risk of injecting issues. We’re careful when making code changes and pay attention to what’s supported on the earliest version of the operating system. We provide options when we need to support disjointed configurations. That’s why, for example, you can still build iOS applications using either OpenGL or Metal, which Flutter automatically chooses at runtime. + +For these reasons —even though we go to great lengths to support testing on OS versions —some of the devices we support are supported on a “best effort” basis. We try to write code that supports the platforms we claim support for, we rely on our team’s commitment to device diversity in ad hoc use, and then look to the community for feedback on how we’re doing in practice. + +We need your help with testing devices in the best effort category. There are tens of thousands (or more!) of SKUs in this category. We test as many as we can and, at times, we source older devices from auction sites to enable us to test and fix device issues that were reported when there is no other way to duplicate the issue. If you find an issue with a particular device, please file an issue on GitHub. + +Finally, some platforms, such as iOS 7, are deprecated. Perhaps they work, but we do not test on deprecated platforms and make no guarantees. We do, however, consider patches for deprecated platforms on a case-by-case basis if we can support the platforms adequately when testing after the patches land. + +Flutter keeps a [web page](https://flutter.dev/docs/development/tools/sdk/release-notes/supported-platforms) of the devices which we test on and those we look to the community for help with. + +## Deciding to deprecate support + +Although we’re committed to supporting every platform version as long as we can, supporting older platforms (or platform configurations, such as OpenGL on Apple A7) comes with a cost, and follows guidance from our upstream partners, including Skia. Flutter’s core engineering team is relatively small, and we’re continually making trade-offs to ensure that what we do has the most beneficial effect for our users. At some point, the cost in terms of engineering, bandwidth for testing in our CI lab and through ad hoc testing, and technical limitations (such as the deprecation of tooling on the part of an OS vendor) can make it difficult or impossible to continue supporting a target platform. + +Before we decide to deprecate support, some of the questions we consider are: + +* How many end users would this eliminate for our developers? + +* What effect does this have on Flutter developers? Are there major partners whose business operations would be significantly affected? + +* What engineering savings are we likely to gain? For example, does the number of applications built with Flutter grow from supporting multiple OS versions? (This is the case, for example, by supporting both OpenGL and Metal in iOS builds.) + +* Does the decision align with our broad adoption and inclusivity goals? + +* Is the deprecation prompted by an external cause, such as a platform vendor deprecating support for a platform version in their build tooling? If so, is it possible to mitigate that deprecation? + +When evaluating whether to deprecate support, we first pose the question to you by starting an issue in GitHub and publishing an associated [Flutter design document](https://flutter.dev/docs/resources/design-docs) that proposes the deprecation, explaining why we’re considering it, and what mitigations might be available. This is a request for comment (RFC) to our community; the public design document is socialized through both our discussion list ([flutter-dev@googlegroups.com](mailto:flutter-dev@googlegroups.com)) and our [Discord](https://github.com/flutter/flutter/wiki/Chat). + +To begin deprecation, we start the RFC process at the delivery of a stable release, as part of the announcement of the stable release. We then leave time for public comments before reducing support for the platform or feature in the subsequent platform release. The RFC is an opportunity for you to provide feedback. Based on the feedback we receive, we will either move forward with the deprecation, or make changes to the proposal, extending the support for a platform version for a longer period. We take feedback about these prospective decisions very seriously, because we recognize that we have a responsibility to you and your product’s users. + +## Request for comment: iOS 8 deprecation + +We are considering deprecating support for iOS 8. All iOS 8 devices can be upgraded to iOS 9. According to both Google and third-party analytics, the number of devices running iOS 8 is extremely small (some report “0.0%”), and Apple is gradually deprecating support for iOS 8 in their tooling. + +We have published a [request for comment about deprecating iOS 8](https://docs.google.com/document/d/1G5L7ux7ofQp3bCSNAt4ovvL5-8PF_rqo2erAVIcHHeg/edit), and we ask for you to review it and participate in the discussion. Our intention is that Flutter 1.22 (our most recent stable release) will be the last stable release to support iOS 8. The RFC includes both the justification and what to do if you’re affected. + +## Request for comment: Android Jelly Bean deprecation + +We are considering deprecating support for Android Jelly Bean because we no longer test devices with that version of Android in our lab, and sourcing reliable devices for lab use is increasingly difficult. Devices running Android Jelly Bean comprise some 0.46% of the user base of Android at this point. + +We have published a [request for comment about deprecating Android ](https://docs.google.com/document/d/1IjlypDrIIGgF2N76P6z8QL2rT5Uuh_rBvqOgDUyhCx8/edit)Jelly Bean, and we ask you to review it and participate in the discussion. Our intention is that Flutter 1.22 (our most recent stable release) will be the last stable release to support Android Jelly Bean. The RFC includes both the justification and what to do if you’re affected. + +## How you can help + +The easiest way to help is to get involved! We appreciate the issues you file regarding the edge cases on various devices that we haven’t encountered. In many cases, they occurred on hardware that we would never have had, even if we doubled or quadrupled our spending on hardware testing. Reporting these issues with reproducible cases and including details about software versions and hardware models is essential for us to track down the source of a problem. + +In addition, we look to you to be involved with the decision making process when the time comes to reduce support for a specific platform. Your insights are invaluable when we consider Flutter’s future. Please participate in our open RFCs when you see one. You are the expert in your particular market, and are closest to your customers. + +We deeply appreciate the trust you’ve given us with your users, and letting us be part of your product. We remain committed to helping you paint your vision to life on as many screens as possible. \ No newline at end of file diff --git a/sites/www/content/blog/put-flutter-to-work/images/17pcNynupdvylRqlRdi6rPQ.webp b/sites/www/content/blog/put-flutter-to-work/images/17pcNynupdvylRqlRdi6rPQ.webp new file mode 100644 index 0000000000..5508292bc6 Binary files /dev/null and b/sites/www/content/blog/put-flutter-to-work/images/17pcNynupdvylRqlRdi6rPQ.webp differ diff --git a/sites/www/content/blog/put-flutter-to-work/images/1gJk1B6Jqynp6QGXirf8VBQ.webp b/sites/www/content/blog/put-flutter-to-work/images/1gJk1B6Jqynp6QGXirf8VBQ.webp new file mode 100644 index 0000000000..014323ebf4 Binary files /dev/null and b/sites/www/content/blog/put-flutter-to-work/images/1gJk1B6Jqynp6QGXirf8VBQ.webp differ diff --git a/sites/www/content/blog/put-flutter-to-work/index.md b/sites/www/content/blog/put-flutter-to-work/index.md new file mode 100644 index 0000000000..56f9c1d2b8 --- /dev/null +++ b/sites/www/content/blog/put-flutter-to-work/index.md @@ -0,0 +1,140 @@ +--- +title: "Put Flutter to Work" +description: "Connect a Flutter feature to a native app and get up and running in just a few lines of code" +publishDate: 2022-05-26 +author: verygoodopensource +image: images/17pcNynupdvylRqlRdi6rPQ.webp +category: spotlight +layout: blog +--- + +<DashImage figure src="images/17pcNynupdvylRqlRdi6rPQ.webp" /> + + +If you’ve ever wanted to try using Flutter, but don’t want to build something from scratch, Flutter’s [add-to-app functionality](https://docs.flutter.dev/development/add-to-app) is a great place to start. To make it even easier to put Flutter to work for you, we teamed up with the Flutter team to create a sample add-to-app prototype to showcase how Flutter can be integrated into a native codebase with minimal effort. Whether you want to take Flutter for a trial run or show your team how Flutter works in a tangible way, this article is for you! + +<DashImage figure src="images/1gJk1B6Jqynp6QGXirf8VBQ.webp" /> + + +## Adding Flutter to a native newsfeed app + +In this tutorial, we’ll show you how to take a Flutter feature and, using Flutter’s add-to-app API, incorporate it into a native app in just a few lines. + +The project we’ll look at is split into two parts. The first part contains three identical newsfeed applications for three separate native platforms: Android, iOS, and the web. The app is interactive, so you can run it on a device and scroll through the articles, click on the news items, and more. The second part of this project is a dialog that pops up when interacting with the app and asks users to submit feedback. This feature, which we’ll call the NPS (Net Promoter Score) module, is built with Flutter. + +First, go to the [example repository](https://github.com/flutter/put-flutter-to-work). Here you’ll see a folder for each platform containing the native code for the newsfeed app. Also in the repository is the `flutter_nps` folder that contains all of the Flutter code for the NPS module. + +## Adding Flutter on the web with Angular + +The Flutter module runs as an `<iframe>` within the native web app. To integrate the feature into the Angular codebase, first run a Flutter build for the web target. This step generates an `index.html` and other necessary files. Copy all of the build files into the Angular app `src` folder. From there, you can reference the build files within the `iframe`. The next time you run the web app, you’ll see the Flutter feature! + +**[View the README for full instructions](https://github.com/flutter/put-flutter-to-work#web).** + +## Adding Flutter to Android with Kotlin + +Now let’s add the NPS module into the Android app. First start a [Flutter activity using a cached engine](https://docs.flutter.dev/development/add-to-app/android/add-flutter-screen#step-3-optional-use-a-cached-flutterengine). As soon as you launch the native news app, the Flutter engine warms up in the background. Then, you’ll start a new activity and point it to the Flutter activity. This ensures a quick transition from the native Kotlin code to Flutter and allow the Flutter feature to work seamlessly within the Android app. + +**[View the README for full instructions](https://github.com/flutter/put-flutter-to-work#android).** + +## Adding Flutter on iOS with SwiftUI + +Finally, we can add the NPS Module into the iOS App. First, embed the compiled Flutter module into your application in Xcode’s build settings. Then, in your application delegate, create an instance of the Flutter engine and start it up. With that done, you’re ready to display Flutter UI wherever needed — just create a `FlutterViewController` using the Flutter Engine and present it. Then run `flutter build ios-framework` with the path. + +**[View the README for full instructions](https://github.com/flutter/put-flutter-to-work#ios).** + +## Put Flutter to work for you (and your team!) + +Now that you have the Flutter code up and running within your app, you can experiment with some of the fun parts of Flutter. The following sections include some ideas for where to begin. + +## Supporting multiple platforms + +In this newsfeed example, you can see how the NPS module supports platform differences. On the web, the module appears as a dialog on top of the newsfeed and reacts to input from a mouse or screen reader. On mobile, the module takes up the full screen space and reacts to input via touch or screen reader. + +Note that the Flutter NPS module contains Material widgets, which automatically handle gesture detection, depending on the user’s device. If using a desktop device, the app receives mouse input, and if using a mobile device, the app receives touch input. + +## Animations + +This prototype includes a few [implicit animations](https://docs.flutter.dev/development/ui/animations/implicit-animations) that are easy to adjust since they are built into the Flutter framework. For example, if you want to make changes to the `AnimatedContainer` widget, simply adjust its properties, such as the duration of the animation, the height of the container, its shape, and color. + +``` +... +return AnimatedContainer( + duration: duration, + height: Spacing.huge, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: isSelected + ? NpsColors.colorSecondary + : NpsColors.colorGrey5, + ), +... +) +``` + + +The NPS module includes a custom page animation transition. Take a look at the `SlideTransition` widget for another animation example that could be customized by updating its duration and other elements. + +``` +SlideTransition( + position: Tween<Offset>( + begin: const Offset(0, 1), + end: Offset.zero, + ).animate(animation), + child: child, +); +``` + + +If you want to take your animations to the next level, you could import the [`animations`](https://pub.dev/packages/animations) package from pub.dev and use some of the fancy, pre-built animations. + +## `Theming` + +It’s also simple to update the theme of the NPS module. Because it uses the built-in Material theming via [`ThemeData`](https://api.flutter.dev/flutter/material/ThemeData-class.html), you can simply update the colors, button style, and font all in one place. For example, to change the `accentColor` and `backgroundColor` of the NPS module with Flutter, update to your desired color using the provided [Material color palette shades](https://api.flutter.dev/flutter/material/Colors-class.html), or your desired custom colors. + +``` +class AppTheme { + ThemeData get theme => ThemeData( + colorScheme: ColorScheme.fromSwatch( + accentColor: NpsColors.colorSecondary, + backgroundColor: NpsColors.colorWhite, + ), + scaffoldBackgroundColor: NpsColors.colorWhite, + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + primary: NpsColors.colorSecondary, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(24), + ), + ).copyWith( + backgroundColor: MaterialStateProperty.resolveWith<Color?>( + (Set<MaterialState> states) { + if (!states.contains(MaterialState.disabled)) { + return NpsColors.colorSecondary; + } else if (states.contains(MaterialState.disabled)) { + return NpsColors.colorWhite; + } + return null; + }, + ), + ), + ), + textTheme: const TextTheme( + headline5: NpsStyles.headline5, + subtitle1: NpsStyles.subtitle1, + bodyText2: NpsStyles.link, + ), + ); +``` + + +## Additional features + +The Flutter NPS module uses [`flutter_bloc`](https://pub.dev/packages/flutter_bloc) for state management to keep track of the user’s score response. Cubit is one of [many options for state management ](https://docs.flutter.dev/development/data-and-backend/state-mgmt/options)when building Flutter applications. The feature also includes [unit and widget tests](https://docs.flutter.dev/testing), which are useful tools to ensure that the code you’re writing is working as intended. Finally, the codebase has [localization](https://docs.flutter.dev/development/accessibility-and-localization/internationalization) support for 78 languages out of the box. This project has [GitHub Actions integration for continuous integration](https://github.com/VGVentures/take-flutter-home/tree/main/.github/workflows) to run formatting, linting, and test phases before merging changes. + +## Backend + +While this prototype does not currently interact with a backend, you could configure it with a backend of your choosing to store the data from the NPS module, or even pull in sample articles for the native newsfeed. One option to explore is Firebase, which integrates seamlessly with Flutter. [See the Firebase documentation to add Firebase to your Flutter app](https://firebase.google.com/docs/flutter/setup?platform=ios). + +Now that you know how to add a Flutter feature into a native web, Android, and iOS codebase, you could follow a similar process to integrate the feature into any native app. See the full [add-to-app documentation](https://docs.flutter.dev/development/add-to-app) for more information. + +## Check out the full code in the [open source repository here](https://github.com/flutter/put-flutter-to-work). \ No newline at end of file diff --git a/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/03MXILeNbFbIFagLu.webp b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/03MXILeNbFbIFagLu.webp new file mode 100644 index 0000000000..e44afed96f Binary files /dev/null and b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/03MXILeNbFbIFagLu.webp differ diff --git a/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/09wOwZ1d4K6Yr3evt.webp b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/09wOwZ1d4K6Yr3evt.webp new file mode 100644 index 0000000000..e7f4ec3082 Binary files /dev/null and b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/09wOwZ1d4K6Yr3evt.webp differ diff --git a/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0AiW2zfqRgJcWNqKn.webp b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0AiW2zfqRgJcWNqKn.webp new file mode 100644 index 0000000000..269c556f50 Binary files /dev/null and b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0AiW2zfqRgJcWNqKn.webp differ diff --git a/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0J9YkkSqIT3dP1CzI.webp b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0J9YkkSqIT3dP1CzI.webp new file mode 100644 index 0000000000..edd69e3d00 Binary files /dev/null and b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0J9YkkSqIT3dP1CzI.webp differ diff --git a/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0OlvN2DIgkndtxdUc.webp b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0OlvN2DIgkndtxdUc.webp new file mode 100644 index 0000000000..039c182844 Binary files /dev/null and b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0OlvN2DIgkndtxdUc.webp differ diff --git a/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0TERDonM4zc_kafRm.webp b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0TERDonM4zc_kafRm.webp new file mode 100644 index 0000000000..4d82c91b99 Binary files /dev/null and b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0TERDonM4zc_kafRm.webp differ diff --git a/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0Zw_zyVq5CfP7Y09o.webp b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0Zw_zyVq5CfP7Y09o.webp new file mode 100644 index 0000000000..c4cc10e687 Binary files /dev/null and b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0Zw_zyVq5CfP7Y09o.webp differ diff --git a/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0onZxbszDrpFkZpHa.webp b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0onZxbszDrpFkZpHa.webp new file mode 100644 index 0000000000..1e10b26afb Binary files /dev/null and b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0onZxbszDrpFkZpHa.webp differ diff --git a/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0qtLcSyZO68tuY6Gk.webp b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0qtLcSyZO68tuY6Gk.webp new file mode 100644 index 0000000000..07faf9b6ee Binary files /dev/null and b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0qtLcSyZO68tuY6Gk.webp differ diff --git a/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0rgxmarvcoWZXajfo.webp b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0rgxmarvcoWZXajfo.webp new file mode 100644 index 0000000000..53d872c289 Binary files /dev/null and b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0rgxmarvcoWZXajfo.webp differ diff --git a/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0rnrluPpgWlvPFw84.webp b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0rnrluPpgWlvPFw84.webp new file mode 100644 index 0000000000..34a8c0f6db Binary files /dev/null and b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0rnrluPpgWlvPFw84.webp differ diff --git a/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0tySCnxRM1tJCRUFQ.webp b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0tySCnxRM1tJCRUFQ.webp new file mode 100644 index 0000000000..21db277fc3 Binary files /dev/null and b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/images/0tySCnxRM1tJCRUFQ.webp differ diff --git a/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/index.md b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/index.md new file mode 100644 index 0000000000..eb8c24a72a --- /dev/null +++ b/sites/www/content/blog/q4-2021-user-survey-results-about-null-safety-ecosystem-text-editing-and-mobile-development/index.md @@ -0,0 +1,113 @@ +--- +title: "Q4 2021 user survey results: about null safety, ecosystem, text editing, and mobile development" +description: "Last November, the Flutter UX team ran the 15th quarterly user survey. The survey was announced via Flutter’s IDE plugins, YouTube…" +publishDate: 2022-02-22 +author: jayoung-lee +image: images/0Zw_zyVq5CfP7Y09o.webp +category: announcements +layout: blog +--- + +Last November, the Flutter UX team ran the 15th quarterly user survey. The survey was announced via Flutter’s IDE plugins, YouTube, Twitter, and the website. Over 2 weeks, we were able to collect 10,105 responses, an all-time high, and we learned that Flutter developers continue to be satisfied with Flutter. The proportion of people positively satisfied with Flutter was 92%, which is stable since Q4 2019. + +Through the survey, the Flutter team also learns what’s going well and what’s not going well with Flutter in more detail, and then sets priorities for future tasks. In this survey, we asked about the four following topics to get an in-depth understanding of these areas. + +* Null safety + +* Ecosystem + +* Text editing + +* Mobile development + +To keep the survey short, we asked different questions to different cohorts, so you might not have seen all four topics. Regardless, we received meaningful, statistically significant feedback around these topics from everyone who participated. As an open source project, we try to be as transparent as possible about what data we collect, and so we’re here to share the results and our learnings. + +## Dart null safety + +The stable version of Dart null safety was [released](https://medium.com/dartlang/announcing-dart-2-12-499a6e689c87) in March 2021. It was one of the largest additions to the language, and we provided a migration tool and a [guide](https://dart.dev/null-safety/migration-guide) to help developers migrate existing code to null safety. We wanted to know how the migration experience has been for you, so that we can do better if we attempt to make similar language changes in the future. + +Overall, 89.5% of those who have used Dart null safety in the past 6 months were satisfied with the feature. Those who had previous experience with null safety (such as Kotlin) were more likely to be satisfied with the feature (+3 percentage points). The biggest benefit of Dart null safety was “reduced bugs in code” (79.4%), followed by “easier-to-understand code” (49.7%). + +<DashImage figure src="images/0rgxmarvcoWZXajfo.webp" alt="*79% of the users had experience with Dart null safety, and 90% were satisfied with it*" caption="*79% of the users had experience with Dart null safety, and 90% were satisfied with it*" /> + + +<DashImage figure src="images/0onZxbszDrpFkZpHa.webp" alt="*Most users (79%) said that Dart null safety reduced bugs in their code*" caption="*Most users (79%) said that Dart null safety reduced bugs in their code*" /> + + +Interestingly, 39.9% said that all their code has used null safety from the start. Given that the survey was fielded only after 8 months since the introduction of stable null safety, the survey showed how quickly Flutter and Dart has grown, welcoming new developers. This result also showed that 59% of those who migrated their code used the migration tool in one way or another (22.0% out of 22.0%+19.6%+6.7%). + +<DashImage figure src="images/0rnrluPpgWlvPFw84.webp" alt="*40% of the users used null safety from the start, and 59% migrated their code to null safety*" caption="*40% of the users used null safety from the start, and 59% migrated their code to null safety*" /> + + +Overall, the survey results supported continuing to evolve the Dart language to make it better. We will continue to make improvements and take your experience into consideration along the way. Please keep the feedback coming! + +## Ecosystem + +As Flutter continues to mature, its package and plugin ecosystem has improved. This improvement includes contributions from the community, as well as programs and promotions like [Flutter Favorites](https://docs.flutter.dev/development/packages-and-plugins/favorites), [Plus plugins](https://plus.fluttercommunity.dev/), and [Package of the Week](https://www.youtube.com/playlist?list=PLjxrf2q8roU1quF6ny8oFHJ2gBdrYN_AK). As shown below, satisfaction with the ecosystem improved by 7 percentage points over the last 2 years. + +<DashImage figure src="images/09wOwZ1d4K6Yr3evt.webp" alt="*Satisfaction with Flutter’s package and plugin ecosystem has increased by 7.5 percent point over the last 2 years*" caption="*Satisfaction with Flutter’s package and plugin ecosystem has increased by 7.5 percent point over the last 2 years*" /> + + +Nevertheless, we continue to hear from you that there still are many packages and plugins that need improvement. To better understand your needs, we asked “Did any Flutter or Dart package decrease your app development productivity in the last 3 months?” When 28.7% of 3,698 answered “Yes” to this question, we presented 42 different packages types, and asked which of the packages decreased productivity. The top 15 packages are below. + +<DashImage figure src="images/0tySCnxRM1tJCRUFQ.webp" alt="*While various packages contributed to decreased productivity of developers. The top choice “Firebase” got votes from 22%.*" caption="*While various packages contributed to decreased productivity of developers. The top choice “Firebase” got votes from 22%.*" /> + + +Firebase is widely used by Flutter users, which contributed to the relatively large number (22.1%). We’re continuing to invest into Firebase plugins (“[FlutterFire](https://firebase.flutter.dev/)” plugins). For example, last December, we [released stable versions of the FlutterFire plugins](https://medium.com/flutter/whats-new-in-flutter-2-8-d085b763d181) and standardized them. It means that all the FlutterFire plugins now use the same underlying version of the SDKs, and developers won’t run into version incompatibility issues when they mix and match the plugins. We plan to monitor this and we’re hoping to see Firebase become a more productive service for developers when we next field this question. + +What’s also notable in the graph above was that 42 common items could not cover all the needs, and 19.2% chose to specify “Other” types of packages and plugins that decrease their productivity. It highlights that there are so many diverse needs that we can only solve as a community. + +For these packages, the biggest challenge was with feature completeness — 23.1% said that packages have incomplete features, so they have to use multiple packages for similar functions or cannot fulfill their needs. + +<DashImage figure src="images/0OlvN2DIgkndtxdUc.webp" alt="*Feature completeness of packages was the biggest contributor to the developers’ decreased productivity*" caption="*Feature completeness of packages was the biggest contributor to the developers’ decreased productivity*" /> + + +The Flutter team has been working to improve the development process for our first-party plugins to make them easier to maintain and improve. For example, `webview_flutter` 3.0 was released recently to address many top feature requests, and we are continuing to invest in addressing the top-voted issues in that and other plugins. + +Even as we ourselves continue to invest in [the](https://pub.dev/publishers/labs.dart.dev/packages) [hundreds](https://pub.dev/publishers/flutter.dev/packages) [of](https://pub.dev/publishers/dart.dev/packages) [packages](https://pub.dev/publishers/tools.dart.dev/packages) that the Flutter and Dart teams own, this is an area where the Flutter community can also play a key role. We all benefit when we all invest in package development! + +## Text editing + +Flutter provides built-in widgets like `TextField`, `TextFormField` (and their Cupertino counterparts), and various packages to allow you to add text editing functionality to your apps. This can become complicated quickly, when features like live filtering and formatting (such as number-only phone number or price input fields), or rich text editors (such as code or math equation editors) are required. + +From this survey, we learned that about 86.7% of developers with intermediate or more experience tried adding text editing to their apps, and the satisfaction with the text editing functionality drop from 82.3% (single-line) and 82.2% (live filtering and formatting) to 69.6% (multi-line) and 66.6% (rich text editor). + +<DashImage figure src="images/0J9YkkSqIT3dP1CzI.webp" alt="*Experts tended to have more experience adding text editing functionality to their apps*" caption="*Experts tended to have more experience adding text editing functionality to their apps*" /> + + +Based on this feedback, the core framework team is planning to build solid basic rich text editing features and APIs to support packages. Also, the team is planning to better advertise existing features and build more examples. In the meantime, you can explore the [design doc](https://docs.google.com/document/d/1PXNd_LwJudiUTnCM2AXRxbd98_p4BlFf6qFhEhv3wvs/edit) on text editing deltas to find useful documentation, as well as [this Github issue](https://github.com/flutter/flutter/issues/87972), to stay up to date on progress. This effort is already well underway with a few low-level APIs now in the master channel. + +We’ve also heard about challenges related to supporting different languages. For example, we heard loud and clear that RTL and bi-di text, such as Arabic, does not work acceptably well. Although there’s no written roadmap, we’re planning to make a big push to better support RTL and bi-di text editing soon. + +## Mobile development + +Because mobile platforms (iOS and Android) were the first ones to be officially supported by Flutter, the majority had experience working on iOS or Android apps in the last 6 months. The team wanted to better understand platform-specific issues, such as challenges related to debugging and releasing, so that the team can better prioritize their engineering effort. + +<DashImage figure src="images/0TERDonM4zc_kafRm.webp" alt="*In the last 6 months, 72% and 91% had developed for iOS and Android, respectively*" caption="*In the last 6 months, 72% and 91% had developed for iOS and Android, respectively*" /> + + +When it comes to debugging, in both platforms, users had to deal with platform specific issues while adding or upgrading packages or plugins most often. While debugging Android issues was less common in many situations, it was notable that debugging to optimize the performance was more common on Android than iOS. + +<DashImage figure src="images/0AiW2zfqRgJcWNqKn.webp" alt="*Debugging platform issues happened more often while adding or upgrading packages or plugins*" caption="*Debugging platform issues happened more often while adding or upgrading packages or plugins*" /> + + +We also learned that dealing with Xcode (iOS) and Gradle (Android) was the most common issue while releasing an app, which matched our hunch. We already made the Flutter CLI show user-friendly error messages for common Gradle errors, which was released in the latest update, [Flutter 2.10](https://medium.com/flutter/whats-new-in-flutter-2-10-5aafb0314b12). + +We will continue to improve the way these build system errors are presented, so that it’s easier to understand and more actionable. For iOS, we will add features to `flutter build ipa` in an upcoming release ([related issue here](https://github.com/flutter/flutter/issues/97179)) that makes release easier. More Xcode settings will be automatically populated in new projects as well ([issue](https://github.com/flutter/flutter/issues/90020)). For Android, we are working on an auto-migration tool, which will be handy to find small discrepancies in Gradle config files. + +<DashImage figure src="images/03MXILeNbFbIFagLu.webp" /> + + +<DashImage figure src="images/0qtLcSyZO68tuY6Gk.webp" alt="*Over 36% of developers dealt with Xcode (iOS) and Gradle (Android) issues while releasing their apps*" caption="*Over 36% of developers dealt with Xcode (iOS) and Gradle (Android) issues while releasing their apps*" /> + + +## What’s next? + +Developers are increasingly using Flutter as part of their main job. The Flutter team will continue to listen and shape our technology using your feedback. If you have any urgent issues, please file an issue on [GitHub](https://github.com/flutter/flutter/issues). + +<DashImage figure src="images/0Zw_zyVq5CfP7Y09o.webp" alt="*The proportion of devs using Flutter for main job has been increasing*" caption="*The proportion of devs using Flutter for main job has been increasing*" /> + + +The Flutter quarterly user survey will continue seeking answers to important questions. You can also post your research ideas on [GitHub](https://github.com/flutter/uxr/discussions/categories/research-ideas). Please keep an eye out for the next survey. Also, consider participating in other research by [signing up for upcoming UX studies](https://docs.google.com/forms/d/e/1FAIpQLSe0i4De809KXVCdljGKrjMj3lxhuzbuFKCtY5PEQPCYtGxFMg/viewform). + +Thanks again to everyone who provided feedback! \ No newline at end of file diff --git a/sites/www/content/blog/racing-forward-at-i-o-2023-with-flutter-and-dart/images/1Zd93zNHQec6C55G3pEJuvA.webp b/sites/www/content/blog/racing-forward-at-i-o-2023-with-flutter-and-dart/images/1Zd93zNHQec6C55G3pEJuvA.webp new file mode 100644 index 0000000000..9e9bbd512e Binary files /dev/null and b/sites/www/content/blog/racing-forward-at-i-o-2023-with-flutter-and-dart/images/1Zd93zNHQec6C55G3pEJuvA.webp differ diff --git a/sites/www/content/blog/racing-forward-at-i-o-2023-with-flutter-and-dart/images/1bzC0ul7jBVhOJiastVGKlw.webp b/sites/www/content/blog/racing-forward-at-i-o-2023-with-flutter-and-dart/images/1bzC0ul7jBVhOJiastVGKlw.webp new file mode 100644 index 0000000000..e5dc48fed0 Binary files /dev/null and b/sites/www/content/blog/racing-forward-at-i-o-2023-with-flutter-and-dart/images/1bzC0ul7jBVhOJiastVGKlw.webp differ diff --git a/sites/www/content/blog/racing-forward-at-i-o-2023-with-flutter-and-dart/index.md b/sites/www/content/blog/racing-forward-at-i-o-2023-with-flutter-and-dart/index.md new file mode 100644 index 0000000000..cc5951c277 --- /dev/null +++ b/sites/www/content/blog/racing-forward-at-i-o-2023-with-flutter-and-dart/index.md @@ -0,0 +1,111 @@ +--- +title: "Racing Forward at I/O 2023 with Flutter and Dart" +description: "Major updates to graphics performance, web integration, developer productivity and security in Flutter 3.10" +publishDate: 2023-05-10 +author: timsneath +image: images/1bzC0ul7jBVhOJiastVGKlw.webp +category: events +layout: blog +--- + +<DashImage figure src="images/1bzC0ul7jBVhOJiastVGKlw.webp" /> + + +We’re *delighted* to be back for this year’s Google I/O, streaming live around the world from near our headquarters in Mountain View, California! + +Just over three months ago, we introduced a bold new roadmap for Flutter and Dart at Flutter Forward in Nairobi, Kenya. At I/O, we’re sharing our progress against that vision with updates across the four themes that we shared: breakthrough graphics experiences, seamless integration with web and platforms, support for new and emerging architectures, and a focus on developer experience. + +<DashImage figure src="images/1Zd93zNHQec6C55G3pEJuvA.webp" /> + + +As you probably know, Flutter is a UI toolkit that enables app developers to build for mobile, web, desktop, and embedded devices from a single codebase. Flutter enables you to build **beautiful** apps, giving you complete control over every pixel on the screen. It’s **fast**, taking advantage of your device’s capabilities with support for hardware-accelerated graphics and natively-compiled machine code. It’s **productive**, with technologies like stateful hot reload that allow you to immediately see code changes in your app. Its **portability** enables you to deploy to a wide variety of platforms with the same source code and without surprises. And it’s fully **open source**, with no cost to license it or need to pay for tools to develop for it. + +## Ongoing Flutter momentum + +Use of Flutter continues to grow both at Google and across the broader industry. Here at Google, teams have deployed Flutter apps across mobile, web and desktop platforms, with examples including: + +* Android’s new [Nearby Share app for Windows](https://www.android.com/better-together/nearby-share-app/), built with Flutter, which allows wireless sharing of photos and documents between Windows and Android devices. + +* The [new Play Console app](https://play.google.com/store/apps/details?id=com.google.android.apps.playconsole), currently in open beta, which lets developers view app statistics and reply to reviews. + +* The [Google Cloud mobile app](https://cloud.google.com/blog/products/management-tools/google-cloud-mobile-app-with-uptime-checks), which lets you monitor your services with a new Flutter-powered experience. + +* [Google Classroom practice sets](https://workspaceupdates.googleblog.com/2023/04/practice-sets-for-google-classroom.html), a new web-based tool for creating and distributing interactive assignments that deliver real-time feedback and help to students. + +On the subject of Classroom, [we announced at Flutter Forward](https://www.youtube.com/watch?v=JVJF_M9bgj4) that the team was building a new version of the mobile app using Flutter. The new version is starting to roll out on iOS now, with new features in an updated version of the Android app coming soon. This version of Classroom uses the latest Flutter technologies, including our new Impeller graphics rendering engine, which enables fast, jank-free UI. + +Rewriting Classroom in Flutter has even improved performance. The new version of the app has faster cold startup time than the old version, as this side-by-side video demonstrates: + +<YoutubeEmbed id="_5CIATSDSPI" title="Comparison of the previous Google Classroom with the new Flutter-powered version" fullwidth="true"/> + + +We are continuing to invest in packages to connect your Flutter app with the rest of the Google developer ecosystem. This includes major updates to our [Google Ads support](https://medium.com/flutter/2023-google-mobile-ads-updates-for-flutter-16b603df9ec9) for native ads; [new Windows and better web support for Firebase](https://github.com/firebase/flutterfire); and new experimental support for [deeper Android interoperability](https://io.google/2023/program/2f02692d-9a41-49c0-8786-1a22b7155628/). + +With Flutter support on six platforms (Android, iOS, web, Windows, macOS, and Linux), **over one million published apps now use Flutter**. They come from all over the world: from [SNCF Connect](https://play.google.com/store/apps/details?id=com.vsct.vsc.mobile.horaireetresa.android&hl=en_US&gl=US), the train travel app for the French railway; to Apple App of the Day winner [SO VEGAN](https://apps.apple.com/us/app/so-vegan/id1572826611); from [Rive’s blazing-fast desktop apps](https://rive.app/downloads) for creating animated graphics to the relationship-empowering [Agapé](https://www.getdailyagape.com/) mobile and tablet app; from the beautiful, [newly-redesigned Global Citizen app](https://www.globalcitizen.org/en/content/new-global-citizen-app-impact-activism-every-day/) to the [new Ubuntu Linux installer](https://9to5linux.com/first-look-at-ubuntu-23-04s-brand-new-desktop-installer-written-in-flutter). We’re so glad to see proof of Flutter’s value! + +## Breakthrough graphics performance with Impeller + +We aspire for Flutter to offer developers and designers immense power for delivering amazing graphical experiences. Over the last few years, we’ve been rebuilding our graphics rendering architecture for speed and power. We call this new engine *Impeller*. +> # “We aspire for Flutter to offer developers and designers immense power for delivering amazing graphical experiences.” + +Since we introduced Impeller on iOS, we’ve expanded testing and our work with early adopters to validate production quality and further tune performance. With today’s Flutter 3.10 release, we’re delighted to announce that [Impeller is now turned on by default for iOS](https://github.com/flutter/flutter/issues/122223), giving apps a big performance boost simply by migrating to the latest version of Flutter. + +With Impeller now enabled for production iOS use, we turn our attention to adding preview support for Android. Just as Impeller on iOS uses the underlying Metal APIs, Android’s implementation of Impeller builds on [Vulkan](https://www.vulkan.org/), which provides low-level APIs for speedy rendering on the underlying graphics hardware. While the vast majority of [modern Android devices](https://developer.android.com/about/dashboards#Vulkan) support Vulkan, we will support a backward-compatible mode for older devices. We’ll be sharing an early preview of Impeller for Android, along with more details about the technical underpinnings of Impeller, in an upcoming blog post. + +## Seamless integration with the web + +As we described at Flutter Forward, we have a different goal than most existing web frameworks. The implementation of Flutter we’re building for the web is explicitly *not* designed to be a general purpose web framework. There are plenty of existing web frameworks, like Angular and React, that fill that space very well. Instead, Flutter is the first framework designed architecturally around new and emerging web technologies like [CanvasKit](https://skia.org/docs/user/modules/canvaskit/) and [WebAssembly](https://webassembly.org/), which particularly suit complex app experiences. +> # “Flutter is the first framework designed architecturally around new and emerging web technologies like CanvasKit and WebAssembly.” + +Since our early releases of Flutter’s web support, we’ve been working hard to improve performance, accessibility and interoperability. + +A major contributor to perceived performance is *load time*, the interval between a page being requested and becoming interactive. In this release, we’ve taken a leap forward, thanks to reductions in the size of CanvasKit on all browsers, and additional optimizations for Chromium-based browsers. In Flutter 3.10, CanvasKit shrank to 1.5MB (from 2.7MB in our previous version). Icon fonts have also been trimmed of unused glyphs, resulting in a 100× reduction in size in most cases. Thanks to these optimizations, we reduced the load time for our default counter app by 42% using a simulated cable connection. + +As previewed at Flutter Forward, we now [support embedding Flutter content in an existing HTML web page](https://flutter-forward-demos.web.app/#/), rather than Flutter occupying the entire page or needing to use inline frames. With Flutter 3.10, we introduced *element embedding*, which allows you to integrate Flutter content just like any other CSS element on the page — for instance, applying complex CSS transitions and transformations. To get started, try one of these samples that use [JavaScript](https://github.com/flutter/samples/tree/main/web_embedding/element_embedding_demo) or wrap Flutter in an [Angular component](https://github.com/flutter/samples/tree/main/web_embedding/ng-flutter). + +Continuing our focus on breakthrough graphics performance, Flutter 3.10 also gains support for [fragment shaders](https://docs.flutter.dev/development/ui/advanced/shaders) on the web. Custom shaders can be used to provide rich graphical effects beyond those that the Flutter SDK provides. A shader is a program authored in a small, Dart-like language, known as [GLSL](https://www.khronos.org/opengl/wiki/Core_Language_(GLSL)), and executed on the user’s GPU. If you want to learn more, check out [our documentation on shaders](https://docs.flutter.dev/ui/advanced/shaders), as well as our [new codelab](https://codelabs.developers.google.com/codelabs/flutter-next-gen-uis#0). + +## Early to new architectures with WebAssembly + +[WebAssembly](https://webassembly.org/) (often abbreviated to Wasm) has been growing in maturity as a platform-neutral binary instruction format for [modern browsers](https://caniuse.com/wasm). On the web, Flutter has been using Wasm to distribute the CanvasKit runtime, while the Dart framework and application code has historically been compiled to JavaScript. We’ve been interested in targeting Wasm, instead of JavaScript, for a while. Until recently, however, Wasm lacked native support for garbage-collected languages like Dart. + +Over the past year we’ve therefore collaborated with several teams across the WebAssembly ecosystem to introduce garbage collection to the standard. This has been achieved via a new extension called [WasmGC](https://github.com/WebAssembly/gc/blob/main/proposals/gc/Overview.md), which now has near-stable implementations in Chromium-based and Firefox browsers. + +WebAssembly excites us with its potential to bring the performance of native code to the web. Dart’s JavaScript compiler, used across millions of lines of code at Google, already generates fast, well-tuned JavaScript. However, switching to Wasm will give us the efficiency of native code with the portability of JavaScript, which will further improve our performance on the web. In some early benchmarks, we’ve seen a boost of 3× for execution speeds, which translates into yet richer web-based experiences. And Wasm couples this with easier integration with code written in other languages like Kotlin and C++. +> # “WebAssembly excites us with its potential to bring the performance of native code to the web.” + +As we wait for browser support to become more pervasive, we have introduced preview support for compiling Flutter apps to WebAssembly in pre-release channels. We’d love you to try it for your own apps and give us early feedback. To learn more, check out [flutter.dev/wasm](https://flutter.dev/wasm). + +## Continued focus on developer experience + +While we hope to delight many with the breakthrough graphics performance and richer web support listed earlier, we also introduce many improvements to developer velocity and productivity in this release. And [our detailed technical blog captures hundreds of improvements to Flutter itself](https://medium.com/flutter/whats-new-in-flutter-3-10-b21db2c38c73), which will be of great interest to existing Flutter developers. + +But perhaps the most significant improvement in this release to the core developer experience is **the release of [Dart 3](https://dart.dev), which is included in Flutter 3.10**. + +Dart 3 completes a long journey to bring sound null safety to the Dart ecosystem. Writing null-safe code protects against a whole class of programming bugs that come from uninitialized values being used without checks. While we’ve supported null safe code since Dart 2.0, we’ve now turned off the legacy “unsafe” mode. As an ecosystem, we’ve been preparing for this for some time, and with 99% of the top 1,000 packages supporting null safety, we believe now is the right time to make the transition. +> # “Dart 3 completes a long journey to bring sound null safety to the Dart ecosystem.” + +Dart 3 introduces many other new features, including records, patterns, and class modifiers, which will improve the readability and fluidity of Dart code. Head to the [main Dart 3 blog](https://medium.com/dartlang/announcing-dart-3-53f065a10635) for more information and examples. Flutter itself is already taking advantage of these new Dart 3 features, so you’ll see improvements to our own codebase as these roll in. And we think you’ll enjoy using them in your own code. + +## SLSA and software supply chain security + +In the modern age, protecting against threats to critical software infrastructure is unfortunately a necessity. So in addition to the headline features listed earlier, our engineering team has [made a priority this year](https://medium.com/flutter/flutter-in-2023-strategy-and-roadmap-60efc8d8b0c7) of investing in security. This investment spans security testing, automation, and supply chain security. +> # “Our team has prioritized investments in security” + +By undertaking the following work, we aim to give businesses further confidence in adopting Flutter: + +* The Open Source Security Foundation [Best Practices Program](https://bestpractices.coreinfrastructure.org/en) serves as a useful benchmark for helping projects adhere to best practices for security and vulnerability management. We are delighted to announce that we have completed [100% of the passing requirements](https://bestpractices.coreinfrastructure.org/en/projects/5631) for this program [passing level](https://bestpractices.coreinfrastructure.org/en/projects/5631). We continue to advance towards demonstrating adherence to [silver](https://bestpractices.coreinfrastructure.org/en/projects/5631?criteria_level=1) and [gold](https://bestpractices.coreinfrastructure.org/en/projects/5631?criteria_level=2) criteria. + +* Flutter has also enabled [OpenSSF Scorecards](https://securityscorecards.dev/) and [Dependabot](https://github.com/dependabot/dependabot-core) on all Flutter critical repositories. OpenSSF Scorecards is a static analysis tool that checks how well your repository adheres to best practices, and creates issues when those practices aren’t being met. Dependabot monitors vulnerabilities in project dependencies and creates pull requests to update them as necessary. Using these tools, the Flutter team has identified and resolved over 300 vulnerabilities across our sites and codelabs. + +* The Flutter and Dart SDKs, along with the release workflows for these SDKs, have recently reached the [SLSA L1](https://slsa.dev/spec/v1.0/levels#build-l1) level. The SLSA (Supply-chain Levels for Software Artifacts) framework helps open source projects maintain strong supply chain security. Reaching SLSA L1 is a great step towards protecting the tools Flutter developers use daily. + +* Lastly, we made many security improvements to our infrastructure. This includes migrating to more secure build and test environments while limiting access to these environments. In addition, we have improved our logging and auditing capabilities for Flutter framework and engine artifacts, providing exceptional protection of our artifacts. These improvements provide the Flutter team far greater insight into how the artifacts we generated are handled during the build process. + +## An open-source project, a work of thousands + +There are thousands of other changes in this release that we hope will delight existing Flutter developers. But it’s worth noting how many of those contributions come from developers outside Google. They include new features; documentation improvements; packages that extend Flutter to horizons we never could have anticipated; and reproducible issue reports and feature requests that give us fresh perspectives on how we can improve. + +Flutter is not a *Google* project, it’s an *all of us* project. We’re so grateful for the diversity and enthusiasm of the community that makes Flutter the product it is. It’s a joy to join you on this mission; the future for Flutter is brighter than ever! + +<YoutubeEmbed id="1J3B-xaoXgw" title="FL020 v9 YT" fullwidth="true"/> diff --git a/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0H6YKXQ1-ToTZ50v2.webp b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0H6YKXQ1-ToTZ50v2.webp new file mode 100644 index 0000000000..4b02b1ecf5 Binary files /dev/null and b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0H6YKXQ1-ToTZ50v2.webp differ diff --git a/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0NiyiyjMKhKn7LJFk.webp b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0NiyiyjMKhKn7LJFk.webp new file mode 100644 index 0000000000..6886d7044f Binary files /dev/null and b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0NiyiyjMKhKn7LJFk.webp differ diff --git a/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0OvXs3e-CZJzW49hA.webp b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0OvXs3e-CZJzW49hA.webp new file mode 100644 index 0000000000..6918aa144f Binary files /dev/null and b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0OvXs3e-CZJzW49hA.webp differ diff --git a/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0W7OF2hEK--gS5XwF.webp b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0W7OF2hEK--gS5XwF.webp new file mode 100644 index 0000000000..51680cb779 Binary files /dev/null and b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0W7OF2hEK--gS5XwF.webp differ diff --git a/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0gC4MeiTr5qVd1fW7.webp b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0gC4MeiTr5qVd1fW7.webp new file mode 100644 index 0000000000..fb08cb8800 Binary files /dev/null and b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0gC4MeiTr5qVd1fW7.webp differ diff --git a/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0n7AfpJ2tNR4CLB0V.webp b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0n7AfpJ2tNR4CLB0V.webp new file mode 100644 index 0000000000..a825982d1d Binary files /dev/null and b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0n7AfpJ2tNR4CLB0V.webp differ diff --git a/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0oE665wZJZhFsz-7s.webp b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0oE665wZJZhFsz-7s.webp new file mode 100644 index 0000000000..e285d0d244 Binary files /dev/null and b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0oE665wZJZhFsz-7s.webp differ diff --git a/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0ypQsScjnj0Mw5Ijg.webp b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0ypQsScjnj0Mw5Ijg.webp new file mode 100644 index 0000000000..61fbc9422e Binary files /dev/null and b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0ypQsScjnj0Mw5Ijg.webp differ diff --git a/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0zJIzYT-PwDfPDcj3.webp b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0zJIzYT-PwDfPDcj3.webp new file mode 100644 index 0000000000..de81af30ad Binary files /dev/null and b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/0zJIzYT-PwDfPDcj3.webp differ diff --git a/sites/www/content/blog/raster-thread-performance-optimization-tips/images/116rv_KQNNU3IsAHCfI3G6A.webp b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/116rv_KQNNU3IsAHCfI3G6A.webp new file mode 100644 index 0000000000..4220938c1c Binary files /dev/null and b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/116rv_KQNNU3IsAHCfI3G6A.webp differ diff --git a/sites/www/content/blog/raster-thread-performance-optimization-tips/images/1VBQwkJIdQBuQl6G9Xy0glg.webp b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/1VBQwkJIdQBuQl6G9Xy0glg.webp new file mode 100644 index 0000000000..6cafa96073 Binary files /dev/null and b/sites/www/content/blog/raster-thread-performance-optimization-tips/images/1VBQwkJIdQBuQl6G9Xy0glg.webp differ diff --git a/sites/www/content/blog/raster-thread-performance-optimization-tips/index.md b/sites/www/content/blog/raster-thread-performance-optimization-tips/index.md new file mode 100644 index 0000000000..12e11cb728 --- /dev/null +++ b/sites/www/content/blog/raster-thread-performance-optimization-tips/index.md @@ -0,0 +1,276 @@ +--- +title: "Raster thread performance optimization tips" +description: "Recently, I sat down to tweak the performance of FlutterFolio, an app that was built as a design showcase for Flutter Engage. With a…" +publishDate: 2021-09-01 +author: filiph +image: images/116rv_KQNNU3IsAHCfI3G6A.webp +category: tutorial +layout: blog +--- + +Recently, I sat down to tweak the performance of FlutterFolio, an app that was built as a design showcase for Flutter Engage. With a single change, I made FlutterFolio significantly faster. + +<DashImage figure src="images/116rv_KQNNU3IsAHCfI3G6A.webp" /> + + +But, first, I had to search for what to change. This article is about that search. + +FlutterFolio is a fully functional app that was created in 6 weeks (!) from design to implementation, for mobile, desktop, and the web. The development team clearly had to cut some corners — no judgement there. The scope of the project and the very short timeline forced them to do that. + +<YoutubeEmbed id="x4xZkdlADWo" title="Flutter Folio Trailer" fullwidth="true"/> + + +In fact, this presents a great opportunity, because the app is more “real life” than all the sample apps I can think of. + +And, performance optimization is better explained on real life apps than on synthetic problems. + +## Step 1: Performance profiling + +What’s the first step of any optimization? Measurement. Knowing that an app seems slow doesn’t cut it. You need to be a bit more precise. Two reasons: + +1. The measurement can point us in the direction of the worst offenders. Every part of every app can be made faster and more efficient. But, you have to start somewhere. Performance profiling lets us see which parts are doing OK and which parts are doing poorly. We can then focus on the parts that are doing poorly, and make more progress in our limited time. + +1. We can compare the before and the after. Sometimes, a code change seems like a good idea, but in reality, it doesn’t make a significant difference. Having a baseline (the before) means that we can quantify the effect of our changes. We can compare the before with the after. + +Performance profiling of apps is hard. I wrote [a long article](https://medium.com/flutter/performance-testing-of-flutter-apps-df7669bb7df7) about it in 2019. So, let’s start simple. We run the app in profile mode, turn on the performance overlay, and use the app, while watching the performance overlay graph. + +<DashImage figure src="images/0H6YKXQ1-ToTZ50v2.webp" /> + + +Immediately, we see that the Raster thread is struggling. + +This happens especially when scrolling through the home page of the app. You should always prioritize the parts of the app where the user spends most of their time or where performance problems are especially noticeable to the user. In other words, if you have two performance problems, and one of them happens on the start screen, and the other one is buried in the settings page, fix the start screen first. + +Let’s look at what the Raster thread is doing. + +## Aside: UI thread versus Raster thread + +Actually, let’s first clarify what the Raster thread does. + +All Flutter apps run on at least two parallel threads: the UI thread and the Raster thread. The UI thread is where you build widgets and where your app logic runs. (You can create isolates, which means that you can run your logic on other threads, but, for simplicity, we’ll ignore that.) The Raster thread is what Flutter uses to _rasterize_ your app. It takes instructions from the UI thread and translates them into something that can be sent to the graphics card. + +To be more concrete, let’s have a look at a build function: + +``` +Widget build(BuildContext context) { + return Image.asset('dash.png'); +} +``` + + +The above code runs on the UI thread. The Flutter framework figures out where to place the widget, what size to give it, and so on — still on the UI thread. + +Then, after Flutter knows everything about the frame, it’s over to the Raster thread. The Raster thread takes the bytes in `dash.png`, resizes the image (if needed), and then applies opacity, blend modes, blur, and so on, until it has the final pixels. The Raster thread then sends the resulting information to the graphics card, and, therefore, to the screen. + +<DashImage figure src="images/0zJIzYT-PwDfPDcj3.webp" /> + + +## Step 2: Digging into the timeline + +OK, back to FlutterFolio. Opening Flutter DevTools lets us look more closely at the timeline. + +<DashImage figure src="images/0ypQsScjnj0Mw5Ijg.webp" /> + + +On the **Performance** tab, you can see that the UI thread (the pale blue bars) is doing quite well, while the Raster thread (the dark blue and red bars) is taking a surprising amount of time for each frame, especially when scrolling down the home page. Therefore, the problem isn’t inefficient build methods or business logic. The problem is asking the Raster thread to do too much. + +The fact that *every frame* spends a long time on the Raster thread tells us something. It says that we’re asking the Raster thread to do some work *over and over* — it’s not something being done once in a while. + +Let’s pick a frame and look at the **Timeline Events** panel.. + +<DashImage figure src="images/0W7OF2hEK--gS5XwF.webp" /> + + +The top part of the timeline, with the light gray background, is the UI thread. Once again, you can see that the UI thread is not the problem. + +Below the UI thread, you see the events on the Raster thread, starting with `GPURasterizer:Draw`. Unfortunately, this is where things get a little hazy. There are a lot of calls to exotic-sounding methods such as `TransformLayer::Preroll`, `OpacityLayer::Preroll`, `PhysicalShapeLayer::Paint` and so on. There are no details about what’s going on in these methods, and these are not names most Flutter developers recognize. + +They are C++ methods from the Flutter Engine. If you feel like it, you can [do a search](https://www.google.com/search?q=PhysicalShapeLayer%3A%3APaint) for these method names, and read the code and the comments to see what’s going on under the hood. Sometimes, this can give you a bit more intuition about what the Raster thread is doing. But, this type of research is not strictly required to find performance problems. (I didn’t do this until relatively recently, and, yet, I was able to optimize the performance of quite a few apps.) + +Then, there’s the long event labeled `SkCanvas::Flush`. It takes 18 milliseconds, which is way above reasonable. Unfortunately, that event also lacks any detailed information, so we’ll need to play detective a bit. + +The `Sk` in `SkCanvas` stands for [Skia](https://skia.org/), the graphics engine that Flutter uses for rendering at the very bottom of its stack. [SkCanvas](https://api.skia.org/classSkCanvas.html) is a low-level C++ class, similar to Flutter’s own [Canvas](https://api.flutter.dev/flutter/dart-ui/Canvas-class.html) (which you might be familiar with if you work with [CustomPaint](https://api.flutter.dev/flutter/widgets/CustomPaint-class.html)). All of your app’s pixels, lines, gradients — all of the UI — goes through SkCanvas. And, `SkCanvas::Flush` is where this class does most of its work after it gathers all the information it needs. The documentation [says](https://api.skia.org/classSkCanvas.html#a3f8ea661c6021dc4dde325aab87870bf) that the Flush method “resolves all pending GPU operations.” + +Let’s review what we learned from the performance timeline: + +* The Raster thread is the major problem. The UI thread is doing relatively fine. + +* While scrolling, the Raster thread takes a long time for _every frame_. Some expensive rasterization work is being done all the time. + +* `SkCanvas::Flush` takes a long time, which means that Skia is doing a lot of homework. + +We do *not* know what that homework is. Let’s review the code. + +## Step 3: Read the code + +Armed with knowledge, let’s look at the source code. If the code is unfamiliar (as it was for me in the case of FlutterFolio), it pays to switch from profile mode to debug mode, and use the [Flutter Inspector](https://flutter.dev/docs/development/tools/devtools/inspector) to jump to the source code of the relevant widgets. + +<DashImage figure src="images/0OvXs3e-CZJzW49hA.webp" /> + + +FlutterFolio’s home page, at least on mobile devices, seems to be, basically, a [vertical PageView populated with BookCoverWidgets](https://github.com/gskinnerTeam/flutter-folio/blob/2bb2101c14ee3f30e11f966e9ce6c50dee600c0b/lib/views/home_page/covers_flow_list_mobile.dart#L36-L40). Looking at `BookCoverWidget`, you can see that it’s essentially [a Stack of various widgets](https://github.com/gskinnerTeam/flutter-folio/blob/2bb2101c14ee3f30e11f966e9ce6c50dee600c0b/lib/views/home_page/book_cover/book_cover.dart#L77-L122), starting with a large image at the bottom, continuing with some animated overlays, the main text content, and ending with a mouse-over overlay at the top. + +``` +child: Stack(fit: StackFit.expand, children: [ + /// ///////////////////////////// + /// Background Image + // Animated scale for when we mouse-over + AnimatedScale( + duration: Times.slow, + begin: 1, + end: isClickable ? 1.1 : 1, + child: BookCoverImage(widget.data), + ), + + /// Black overlay, fades out on mouseOver + AnimatedContainer(duration: Times.slow, + color: Colors.black.withOpacity(overlayOpacity)), + + /// When in large mode, show some gradients, + /// should sit under the Text elements + if (widget.largeMode) ...[ + FadeInLeft( + duration: Times.slower, + child: _SideGradient(Colors.black), + ), + FadeInUp(child: _BottomGradientLg(Colors.black)) + ] else ...[ + FadeInUp(child: _BottomGradientSm(Colors.black)), + ], + + /// Sit under the text content, and unfocus when tapped. + GestureDetector(behavior: HitTestBehavior.translucent, + onTap: InputUtils.unFocus), + + /// BookContent, shows either the Large cover or Small + Align( + alignment: widget.topTitle ? Alignment.topLeft : Alignment.bottomLeft, + // Tween the padding depending on which mode we're in + child: AnimatedContainer( + duration: Times.slow, + padding: EdgeInsets.all(widget.largeMode ? Insets.offset : Insets.sm), + child: (widget.largeMode) + ? LargeBookCover(widget.data) + : SmallBookCover(widget.data, topTitle: widget.topTitle), + ), + ), + +/// Mouse-over effect + if (isClickable) ...[ + Positioned.fill(child: FadeIn(child: RoundedBorder(color: theme.accent1, ignorePointer: false))), + ], +]), +``` + + +Now, remember: you’re looking for something that happens in every frame (that is, it’s always present), and that is potentially expensive for the Skia renderer to draw (images, blurs, blends, and so on). + +## Step 4: Drill down + +Now, you need to drill down to find widgets that might be problematic. One way to do this is to temporarily remove various widgets from the app and see what kind of effect this has on the performance. + +Remember, the first child of the `Stack` is the background, and every subsequent child is a layer on top of the previous widgets. So, the first child is the background image, represented by `BookCoverImage`. You could remove it, but the homepage would look like this: + +<DashImage figure src="images/0gC4MeiTr5qVd1fW7.webp" /> + + +That defeats the purpose of the whole page. Looking closer at `BookCoverImage`, you can see that it’s just a simple wrapper around `Image`. With one notable exception (mentioned later in this article), there isn’t much that could be improved here. + +Moving on, there’s this code: + +``` +/// Black overlay, fades out on mouseOver +AnimatedContainer(duration: Times.slow, + color: Colors.black.withOpacity(overlayOpacity)), +``` + + +This is a widget that covers the whole image with a layer of transparent black. `overlayOpacity` is 0 by default (and most of the time), so this layer is fully transparent. Hmm. Let’s remove it, and run the app in profile mode again. + +<DashImage figure src="images/0NiyiyjMKhKn7LJFk.webp" /> + + +Interesting! The Raster thread still takes quite a lot of load, but there is a major performance improvement. + +I decided to implement a more robust performance profiling tool for FlutterFolio, so that I can prove that the improvement is real and not just a fluke. This change gives me an impressive 20% less CPU time spent rasterizing overall and 50% less potential jank. + +<DashImage figure src="images/0oE665wZJZhFsz-7s.webp" /> + + +All in all, this is a massive change for removing a single widget that does nothing most of the time. + +The [fix](https://github.com/gskinnerTeam/flutter-folio/pull/62) is simple: + +``` +/// Black overlay, fades out on mouseOver +if (overlayOpacity > 0) + AnimatedContainer(duration: Times.slow, + color: Colors.black.withOpacity(overlayOpacity)), +``` + + +Now, you only add the transparent overlay when it has non-zero opacity (that is, it’s at least partially visible). You avoid the (very common!) scenario in which a completely transparent layer is created and rasterized, but doesn’t have any effect. + +And, just like that, the app is both smoother and more battery efficient. + +**Note:** Why do you need to do this? Shouldn’t Flutter be smart enough to make this optimization for us? Read the issue [here](https://github.com/flutter/flutter/pull/72526#issuecomment-749185938) to learn why it can’t. And why is the transparent opacity slow in the first place? That is out of scope of this article but it has to do with a `BackdropFilter` widget further up the `Stack` that interacts with every widget below it. + +The main point of this article isn’t to teach you about this particular performance pitfall. You might never see it again. My goal is to teach you how to optimize Raster thread performance in general. + +## Step 5: Generalize + +Before moving on to a completely different issue, it’s often a good idea to look elsewhere in the project for similar problems. Are there other places in our app with large-area overlays? Can you avoid them? + +In this case, the next few lines create large gradients that fade in as you scroll: + +``` +/// When in large mode, show some gradients, +/// should sit under the Text elements +if (widget.largeMode) ...[ + FadeInLeft( + duration: Times.slower, + child: _SideGradient(Colors.black), + ), + FadeInUp(child: _BottomGradientLg(Colors.black)) +] else ...[ + FadeInUp(child: _BottomGradientSm(Colors.black)), +], +``` + + +And, sure enough, removing these animated, almost-full-screen gradients significantly improves the scrolling performance. Unfortunately, in this case, the solution isn’t as simple as with the previous example. These gradients aren’t invisible. They start fading in as soon as the user reaches that cover. Removing them *does* make a visual difference. + +One idea is to delay the fade in for a bit, so that the animation only starts when the user lands on a specific `BookCover`. That way, you can lighten the Raster thread’s load while the user is scrolling, and hopefully avoid some of the potential jank. + +But, that’s a change to the app’s motion design, and so it needs to be discussed with the larger team. Many performance optimizations will fall into this category. Performance optimization is often an exercise in compromise. + +## Repeat Steps 2–5 until happy + +We’ve only looked at one type of problem so far. There are always more. + +Here is one idea about what to do next: are the app’s image assets too large? Remember, the Raster thread is in charge of taking image bytes, decoding them, resizing, applying filters and so on. If it’s loading and resizing a 20 MB high-definition image into a tiny avatar image on a screen, then you’re wasting resources. + +When your app is running in debug mode, you can use Flutter Inspector to [invert oversized images](https://api.flutter.dev/flutter/painting/debugInvertOversizedImages.html). + +<DashImage figure src="images/1VBQwkJIdQBuQl6G9Xy0glg.webp" /> + + +This will color invert and flip all images in your app that are too large for their actual use. You can then peruse the app and watch for unnatural changes. + +<DashImage figure src="images/0n7AfpJ2tNR4CLB0V.webp" /> + + +The debug mode also reports an error every time it encounters such an image, for example: +> [ERROR] Image assets/images/empty-background.png has a display size of 411×706 but a decode size of 2560×1600, which uses an additional 19818KB. + +The fix here isn’t straightforward, though. On mobile, you don’t need a 2560×1600 image, but on desktop, you might. And, remember, FlutterFolio is an app that runs on all of Flutter’s targets, including desktop. If in doubt, [read the API docs](https://api.flutter.dev/flutter/painting/debugInvertOversizedImages.html). + +## Conclusion + +As you can see, optimizing performance is an art and a science. Robust benchmarking helps and so does an intimate understanding of the framework and its built-in widgets. + +Ultimately, practice makes perfect. Optimize enough apps, and you get better. + +Happy hunting. + +**UPDATE 2025:** I’m writing a Flutter performance book. You can read my drafts [here](https://filiph.net/flutter-performance/) and you can subscribe to get notified when the book’s published [here](https://filiph.net/await). \ No newline at end of file diff --git a/sites/www/content/blog/rich-and-dynamic-user-interfaces-with-flutter-and-generative-ui/images/12R8k2Qbswy4G7h3yYIyk1A.webp b/sites/www/content/blog/rich-and-dynamic-user-interfaces-with-flutter-and-generative-ui/images/12R8k2Qbswy4G7h3yYIyk1A.webp new file mode 100644 index 0000000000..b217fe20e6 Binary files /dev/null and b/sites/www/content/blog/rich-and-dynamic-user-interfaces-with-flutter-and-generative-ui/images/12R8k2Qbswy4G7h3yYIyk1A.webp differ diff --git a/sites/www/content/blog/rich-and-dynamic-user-interfaces-with-flutter-and-generative-ui/images/1fvTGsrJGfrH1JCeBnMyHVA.webp b/sites/www/content/blog/rich-and-dynamic-user-interfaces-with-flutter-and-generative-ui/images/1fvTGsrJGfrH1JCeBnMyHVA.webp new file mode 100644 index 0000000000..24e36f8655 Binary files /dev/null and b/sites/www/content/blog/rich-and-dynamic-user-interfaces-with-flutter-and-generative-ui/images/1fvTGsrJGfrH1JCeBnMyHVA.webp differ diff --git a/sites/www/content/blog/rich-and-dynamic-user-interfaces-with-flutter-and-generative-ui/images/1hxPwEhX6gYuF8CO2lGydkg.webp b/sites/www/content/blog/rich-and-dynamic-user-interfaces-with-flutter-and-generative-ui/images/1hxPwEhX6gYuF8CO2lGydkg.webp new file mode 100644 index 0000000000..e1f54172be Binary files /dev/null and b/sites/www/content/blog/rich-and-dynamic-user-interfaces-with-flutter-and-generative-ui/images/1hxPwEhX6gYuF8CO2lGydkg.webp differ diff --git a/sites/www/content/blog/rich-and-dynamic-user-interfaces-with-flutter-and-generative-ui/images/1yZgCoXy4aivZfEjgTJMOrA.webp b/sites/www/content/blog/rich-and-dynamic-user-interfaces-with-flutter-and-generative-ui/images/1yZgCoXy4aivZfEjgTJMOrA.webp new file mode 100644 index 0000000000..dce845ee77 Binary files /dev/null and b/sites/www/content/blog/rich-and-dynamic-user-interfaces-with-flutter-and-generative-ui/images/1yZgCoXy4aivZfEjgTJMOrA.webp differ diff --git a/sites/www/content/blog/rich-and-dynamic-user-interfaces-with-flutter-and-generative-ui/index.md b/sites/www/content/blog/rich-and-dynamic-user-interfaces-with-flutter-and-generative-ui/index.md new file mode 100644 index 0000000000..28f8b6751c --- /dev/null +++ b/sites/www/content/blog/rich-and-dynamic-user-interfaces-with-flutter-and-generative-ui/index.md @@ -0,0 +1,85 @@ +--- +title: "Rich and dynamic user interfaces with Flutter and generative UI" +description: "We’re thrilled to introduce GenUI SDK for Flutter, now available in alpha on pub.dev." +publishDate: 2025-11-18 +author: abdallahshaban557 +image: images/12R8k2Qbswy4G7h3yYIyk1A.webp +layout: blog +--- + +<DashImage figure src="images/12R8k2Qbswy4G7h3yYIyk1A.webp" alt="Introducing the GenUI SDK for Flutter" caption="Introducing the GenUI SDK for Flutter" /> + + +## Rich and dynamic user interfaces with Flutter and generative UI + +We’re thrilled to introduce GenUI SDK for Flutter, now available in alpha on [pub.dev](https://pub.dev/packages/genui). This new SDK for Flutter helps you generate dynamic, personalized UI with Gemini (or other LLMs) to significantly improve the usability and satisfaction of your GenAI and agent-based user experiences, which also adheres to your established brand guidelines and using your own widget catalog. + +Today’s app users expect not only beautiful and fast UIs, but also more dynamic, deeply personalized, and adaptive experiences. We are building GenUI SDK for Flutter, a suite of packages, to make it easier for you to build and deliver this new generation of experiences to all of your users. + +<DashImage figure src="images/1yZgCoXy4aivZfEjgTJMOrA.webp" alt="From traditional chatbots to dynamic, interactive, graphical experiences" caption="From traditional chatbots to dynamic, interactive, graphical experiences" /> + + +A great example of a powerful generative UI experience built with Flutter and Gemini is the new experimental [visual layout](https://blog.google/products/gemini/gemini-3-gemini-app/) feature in the Gemini app ([App Store](https://apps.apple.com/us/app/google-gemini/id6477489729), [Play Store](https://play.google.com/store/apps/details?id=com.google.android.apps.bard&hl=en_US)), rolling out today. It generates an immersive, magazine-style view of the information, complete with photos and modules. These generative elements don’t just look great; they are interactive and help you further customize the experience. Gemini’s new visual layout feature inspired us to build the GenUI SDK for Flutter, so that anyone can also build the same kinds of dynamic, interactive, generative experiences. + +## How does GenUI SDK for Flutter work? + +At its core, the GenUI SDK for Flutter is an orchestration layer. It coordinates the flow of information between your user, your Flutter widgets, and an AI agent, transforming text-based conversations into rich, interactive experiences. + +<DashImage figure src="images/1hxPwEhX6gYuF8CO2lGydkg.webp" /> + + +1. **The app sends a request:** The user provides a prompt (for example, “Help me plan a trip to Tokyo!”). Your app calls your AI agent with the prompt and additional information about the catalog of widgets it can use and compose together in response. + +1. **The AI agent generates both content and UI to display it:** The AI agent generates a response, but instead of just relying on text, it uses tools provided by the GenUI SDK to describe the UI most suited to the content it generated. + +1. **UI is dynamically rendered:** The GenUI SDK receives the response, deserializes it, and builds the appropriate widgets. Some are informational, some are layout, and some can be interactive (and might include sliders, buttons, or a date picker). + +1. **The user interacts with the dynamic UI:** The user taps on buttons and other controls, which can trigger followup requests to the agent, which result in more UI updates, and so on. + +This loop enables you to move beyond simple text responses. By defining your own custom widgets, the GenUI SDK for Flutter ensures the dynamically generated UI is compliant with your branding guidelines. + +For a more detailed explanation and walkthrough of the GenUI SDK for Flutter, check out the following video: + +<YoutubeEmbed id="nWr6eZKM6no" title="Getting started with GenUI" fullwidth="true"/> + + +### The UI protocol under the covers + +The GenUI SDK for Flutter is built around the [upcoming A2UI protocol](https://a2ui.org/) in collaboration with Google’s [A2A](https://a2a-protocol.org/) team and the [Opal](https://opal.google/) team at Google Labs. A2UI is a new (soon to be open source) toolkit to facilitate implementing LLM-generated UIs in any framework. We use A2UI as the serialization format for UI that is generated by the LLM, and the GenUI SDK for Flutter is using an early version of an A2UI renderer. + +## How do I get started with building GenUI experiences? + +We want to make it easy to experiment with the GenUI SDK for Flutter, so we’ve bundled in ***content generators*** for LLMs, ***sample apps*** to see it in action, and ***markdown docs*** for coding agents (and humans!) + +* First, grab the main pub package for [genui](https://pub.dev/packages/genui). + +* Next, choose a content generator. The following are the content generator options we provide that support a variety of use cases: +- [Google Gemini API](https://pub.dev/packages/genui_google_generative_ai): Useful for experimentation and local testing as you’re mapping out your experience. +- [Firebase AI logic](https://pub.dev/packages/genui_firebase_ai): Useful for production apps where interactions with the LLM are all in your Flutter client, without requiring a server. Firebase also makes it easier to ship your AI features securely since Firebase handles the management of your Gemini API key. +- [A2UI](https://pub.dev/packages/genui_a2ui): Useful for client/server architectures where your agent is running on the server. +- Build your own: Of course, you can also build your own adapter to connect to your preferred LLM provider. Expect more from us and the community soon. + +To make it easy for your coding agent to build with the GenUI SDK for Flutter, we also included [markdown docs](https://github.com/flutter/genui/tree/main/packages/genui/.guides). These markdown files teach your coding agent how to use the various GenUI SDK APIs. + +The [flutter/genui repo](https://github.com/flutter/genui) has sample apps that you can use to give you a good starting point for building GenUI experiences. The following video shows the [Landscape design app](https://github.com/flutter/genui/tree/main/examples/verdure) , which gives you an example of a Flutter client interacting with a Python-based server using A2A (Agent-to-Agent) and ADK, with UI generated dynamically by the AI agent. In this demo app, the user can upload a picture of their yard, Gemini analyzes it, and then through the GenUI SDK for Flutter provides a custom UI for the user to enter their constraints and requirements for updating their yard’s landscaping. Once the user inputs their preferences, then they would see a result that takes into account their selections. This app demonstrates how a GenUI-based experience improves the collaboration between human and agent. + +<DashImage figure src="images/1fvTGsrJGfrH1JCeBnMyHVA.webp" alt="Landscape design app demo" caption="Landscape design app demo" /> + + +## What’s coming next? + +We are building the GenUI SDK for Flutter to help you deliver personalized, dynamic, AI-driven app experiences across mobile, web, and desktop from a single codebase. Here are some of the things we are considering for our roadmap: + +* **Agent Development Kit (ADK) plugin:** turnkey integration with ADK. + +* **Expanded LLM framework support:** Official adapters for other popular AI models and backend frameworks. + +* **Reduced perceived latency:** Progressively rendering standard UI components as the LLM generates them, rather than waiting for the full response. + +* **Full-screen composition:** Moving beyond individual widgets in a stream to let the LLM compose entire screen layouts and handle complex app navigation based on user intent. + +* **Dart bytecode:** Providing true server-driven UI by allowing the backend to send entirely new widget logic over the wire, not just configurations for widgets already in your binary. + +* **Genkit integration:** Integration with [Genkit](https://genkit.dev/), an open-source AI framework. + +The project is still in its early phases, so we’re excited to hear your thoughts and [feedback](https://github.com/flutter/genui/discussions). We can’t wait to see the GenUI experiences you build with Flutter! \ No newline at end of file diff --git a/sites/www/content/blog/running-unit-tests-as-part-of-your-everyday-flutter-workflow/images/1GKGBr7v55mRKhQyOkNaFoA.webp b/sites/www/content/blog/running-unit-tests-as-part-of-your-everyday-flutter-workflow/images/1GKGBr7v55mRKhQyOkNaFoA.webp new file mode 100644 index 0000000000..d6462d7477 Binary files /dev/null and b/sites/www/content/blog/running-unit-tests-as-part-of-your-everyday-flutter-workflow/images/1GKGBr7v55mRKhQyOkNaFoA.webp differ diff --git a/sites/www/content/blog/running-unit-tests-as-part-of-your-everyday-flutter-workflow/images/1V_EILJ0CmVIDWCBbK-VmtQ.webp b/sites/www/content/blog/running-unit-tests-as-part-of-your-everyday-flutter-workflow/images/1V_EILJ0CmVIDWCBbK-VmtQ.webp new file mode 100644 index 0000000000..0d6bc94764 Binary files /dev/null and b/sites/www/content/blog/running-unit-tests-as-part-of-your-everyday-flutter-workflow/images/1V_EILJ0CmVIDWCBbK-VmtQ.webp differ diff --git a/sites/www/content/blog/running-unit-tests-as-part-of-your-everyday-flutter-workflow/images/1lCTc5ZARrw1ops8H6zA37A.webp b/sites/www/content/blog/running-unit-tests-as-part-of-your-everyday-flutter-workflow/images/1lCTc5ZARrw1ops8H6zA37A.webp new file mode 100644 index 0000000000..b7c5b8e181 Binary files /dev/null and b/sites/www/content/blog/running-unit-tests-as-part-of-your-everyday-flutter-workflow/images/1lCTc5ZARrw1ops8H6zA37A.webp differ diff --git a/sites/www/content/blog/running-unit-tests-as-part-of-your-everyday-flutter-workflow/images/1pDCMYhDu_XxFEzUNRreupA.webp b/sites/www/content/blog/running-unit-tests-as-part-of-your-everyday-flutter-workflow/images/1pDCMYhDu_XxFEzUNRreupA.webp new file mode 100644 index 0000000000..1abdb1daa9 Binary files /dev/null and b/sites/www/content/blog/running-unit-tests-as-part-of-your-everyday-flutter-workflow/images/1pDCMYhDu_XxFEzUNRreupA.webp differ diff --git a/sites/www/content/blog/running-unit-tests-as-part-of-your-everyday-flutter-workflow/index.md b/sites/www/content/blog/running-unit-tests-as-part-of-your-everyday-flutter-workflow/index.md new file mode 100644 index 0000000000..a76e5bf3e6 --- /dev/null +++ b/sites/www/content/blog/running-unit-tests-as-part-of-your-everyday-flutter-workflow/index.md @@ -0,0 +1,72 @@ +--- +title: "Running unit tests as part of your everyday Flutter workflow" +description: "So you have gotten started with Flutter, have your app prototype up and running, and have started thinking about how you’d test your app…" +publishDate: 2018-03-06 +author: devoncarew +image: images/1GKGBr7v55mRKhQyOkNaFoA.webp +category: tutorial +layout: blog +--- + +So you have gotten started with Flutter, have your app prototype up and running, and have started thinking about how you’d test your app. This guide will give you the quick and dirty of how to run and debug your tests. + +The good news is that you already have a helpful starting example of a Flutter unit test! The **flutter create** template ships with a sample unit test: + +```dart +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:foo_bar/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(new MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} + +``` + +You can see that it verifies the behavior of the counter for the sample app. You may not have noticed the ‘run’ icons in the editor’s gutter: + +<DashImage figure src="images/1V_EILJ0CmVIDWCBbK-VmtQ.webp" /> + + +This is the Flutter plugin parsing your unit test code and inserting handy ‘run test’ actions directly into your editor. From IntelliJ or Android Studio, click on the ‘run’ icon and select either the option to run or debug your test: + +<DashImage figure src="images/1pDCMYhDu_XxFEzUNRreupA.webp" /> + + +The tests will run, and the results will be displayed in the standard IntelliJ test result view: + +<DashImage figure src="images/1GKGBr7v55mRKhQyOkNaFoA.webp" /> + + +To debug your test, just set a breakpoint where you’d like to pause the test and select the **Debug ‘test…’** option from the editor’s gutter run action. Your test will run and break at the breakpoint, letting you inspect variables and step through the code. + +<DashImage figure src="images/1lCTc5ZARrw1ops8H6zA37A.webp" /> + + +## VS Code + +If you develop with VS Code, follow issue [#636](https://github.com/Dart-Code/Dart-Code/issues/636) to track progress in adding support for **flutter test** tests there. You can also show your interest by added a thumbs up to the issues! It’ll help with prioritizing among feature requests. + +## For more info… + +That’s it! For more info about testing your Flutter apps, see: + +* [flutter.io/testing](https://flutter.io/testing) — a guide to both unit testing your widgets with **flutter test**, and integration testing your app with **flutter drive** + +* [Test Flutter apps on Travis](https://medium.com/flutter-io/test-flutter-apps-on-travis-3fd5142ecd8c) — an article about running your Flutter tests on Travis, by [Yegor Jbanov](https://medium.com/@yegorj?source=post_header_lockup) \ No newline at end of file diff --git a/sites/www/content/blog/save-the-date-flutter-day-june-25-2020/images/0RDn0t0fyTKSy2x_k.webp b/sites/www/content/blog/save-the-date-flutter-day-june-25-2020/images/0RDn0t0fyTKSy2x_k.webp new file mode 100644 index 0000000000..5a6bbcf178 Binary files /dev/null and b/sites/www/content/blog/save-the-date-flutter-day-june-25-2020/images/0RDn0t0fyTKSy2x_k.webp differ diff --git a/sites/www/content/blog/save-the-date-flutter-day-june-25-2020/images/0siIoSIIiPLyDxPTP.webp b/sites/www/content/blog/save-the-date-flutter-day-june-25-2020/images/0siIoSIIiPLyDxPTP.webp new file mode 100644 index 0000000000..bd92163768 Binary files /dev/null and b/sites/www/content/blog/save-the-date-flutter-day-june-25-2020/images/0siIoSIIiPLyDxPTP.webp differ diff --git a/sites/www/content/blog/save-the-date-flutter-day-june-25-2020/images/0vValokazzfj52F2N.webp b/sites/www/content/blog/save-the-date-flutter-day-june-25-2020/images/0vValokazzfj52F2N.webp new file mode 100644 index 0000000000..c8b155261d Binary files /dev/null and b/sites/www/content/blog/save-the-date-flutter-day-june-25-2020/images/0vValokazzfj52F2N.webp differ diff --git a/sites/www/content/blog/save-the-date-flutter-day-june-25-2020/index.md b/sites/www/content/blog/save-the-date-flutter-day-june-25-2020/index.md new file mode 100644 index 0000000000..28c2350ef3 --- /dev/null +++ b/sites/www/content/blog/save-the-date-flutter-day-june-25-2020/index.md @@ -0,0 +1,48 @@ +--- +title: "Save the Date: Flutter Day, June 25, 2020" +description: "As a team, we’re used to plenty of travel, hanging out at various events and user groups, and connecting with folks in person; we miss our…" +publishDate: 2020-05-21 +author: csells +image: images/0RDn0t0fyTKSy2x_k.webp +category: events +layout: blog +--- + +### 24 hours of supported codelabs with video streams from the Flutter team + +As a team, we’re used to plenty of travel, hanging out at various events and user groups, and connecting with folks in person; we miss our time with the Flutter community! So, while we’re not able to get together face-to-face, we’re going to do the next best thing: bring the gathering to you with a dedicated day of Flutter. + +Flutter Day ([g.co/FlutterDay](http://g.co/FlutterDay)) starts at 9am Pacific Time on June 25th, 2020 and goes for 24 hours worldwide, enjoying all things Flutter: the technology, ecosystem and most importantly, the community. + +First and foremost, we’ve got codelabs, both existing ones that have been lovingly updated and brand new ones! Whether you’re new to Flutter or a seasoned pro, you’re going to find something you like in this list. And to make sure you get the help you need, we’ll have a 24-hour support channel for you to get your codelab questions answered. + +Then, we’ve got three one-hour video streams for you spread throughout the day so that you’ll find something no matter what time zone you’re in. We’ll recap and expand on some of the news we’ve announced over the last few weeks for both Flutter and Dart, focusing on front-end design and development with Flutter and showing some demos that we think you’ll like. And we want to hear from you — we’ve dedicated some time for the ever-popular #AskFlutter, where you get to connect with members of the team on topics that you want to hear more about. + +We have a great lineup, including engineers, PMs, and developer advocates from the Flutter and Dart teams. + +<DashImage figure src="images/0siIoSIIiPLyDxPTP.webp" /> + + +For the Flutter Day details and to let us know that you’re interested in participating, [register on the Flutter Day website today](http://g.co/flutterday)! + +## Flutter Day Near You + +Of course, Flutter Day is for everyone, so we’re also coordinating with local Flutter user groups around the world to gather all of the virtual meetups going on to recognize Flutter Day. + +<DashImage figure src="images/0RDn0t0fyTKSy2x_k.webp" /> + + +No matter where you live, you’ll be able to find a local Flutter virtual meetup near you. + +## Get Ready for Hack20 + +And as if that weren’t enough, this year we’re teaming up with [the #Hack20 International Flutter Hackathon](https://flutterhackathon.com/#/). + +<DashImage figure src="images/0vValokazzfj52F2N.webp" /> + + +Right after you’re done with the day of Flutter codelabs, you’re ready to jump into a weekend of Flutter hacking with prizes for the best app and featuring mentors from the Flutter community. + +## Something for Everyone + +Whether you’re trying Flutter for the first time, or a seasoned veteran, Flutter Day has something for you. Hang out with Flutter fans from around the world, get your Flutter questions answered, enjoy some Flutter content from around the world, and then spend the weekend at a hackathon. Don’t forget to register your interest and see the details at [g.co/FlutterDay](http://g.co/FlutterDay) today. \ No newline at end of file diff --git a/sites/www/content/blog/saying-goodbye-to-cocoapods-swift-package-manager-is-soon-the-default-in-flutter/images/12A_CUygcOEFYUv5ms5KAJw.webp b/sites/www/content/blog/saying-goodbye-to-cocoapods-swift-package-manager-is-soon-the-default-in-flutter/images/12A_CUygcOEFYUv5ms5KAJw.webp new file mode 100644 index 0000000000..7ddb8cdc10 Binary files /dev/null and b/sites/www/content/blog/saying-goodbye-to-cocoapods-swift-package-manager-is-soon-the-default-in-flutter/images/12A_CUygcOEFYUv5ms5KAJw.webp differ diff --git a/sites/www/content/blog/saying-goodbye-to-cocoapods-swift-package-manager-is-soon-the-default-in-flutter/index.md b/sites/www/content/blog/saying-goodbye-to-cocoapods-swift-package-manager-is-soon-the-default-in-flutter/index.md new file mode 100644 index 0000000000..88b2a6275e --- /dev/null +++ b/sites/www/content/blog/saying-goodbye-to-cocoapods-swift-package-manager-is-soon-the-default-in-flutter/index.md @@ -0,0 +1,46 @@ +--- +title: "Saying goodbye to CocoaPods: Swift Package Manager is soon the default in Flutter!" +description: "Starting with the next stable Flutter release, 3.44, Swift Package Manager (SwiftPM) replaces CocoaPods as the default dependency manager…" +publishDate: 2026-04-30 +author: magder +image: images/12A_CUygcOEFYUv5ms5KAJw.webp +layout: blog +--- + +<DashImage figure src="images/12A_CUygcOEFYUv5ms5KAJw.webp" alt="Dash migration!" caption="Dash migration!" /> + + +## Saying goodbye to CocoaPods: Swift Package Manager is soon the default in Flutter! + +Starting with the next stable Flutter release, 3.44, Swift Package Manager (SwiftPM) replaces CocoaPods as the default dependency manager for iOS and macOS apps. *This means no more messing around with Ruby or CocoaPods installations just to get your app running!* + +CocoaPods is officially in maintenance mode, and its registry will permanently [become read-only on December 2, 2026](https://blog.cocoapods.org/CocoaPods-Specs-Repo/). While existing builds will still work, no new versions or pods will be added to the trunk after this date. To ensure that your apps continue receiving dependency updates and to provide access to the Swift package ecosystem, Flutter is transitioning to Apple’s supported dependency management solution: Swift Package Manager. + +**If you’ve already migrated your plugin(s) to use SwiftPM, read the “Plugin developers” section below for new migration requirements.** + +Here is how to manage the transition. + +## App developers + +For app developers, the Flutter CLI handles the migration. When you run or build your iOS or macOS app, the CLI automatically updates your Xcode project to use Swift Package Manager. Check out the [Flutter migration docs for app developers](https://docs.flutter.dev/packages-and-plugins/swift-package-manager/for-app-developers) for more details. + +If your app relies on plugins that haven’t adopted Swift Package Manager yet, Flutter will print a warning listing exactly which of your dependencies are unsupported. Flutter will temporarily fall back to CocoaPods for plugins that have not adopted Swift packages yet. Because CocoaPods support will eventually be removed entirely, if a plugin hasn’t updated and breaks your build, file an issue with the dependency’s maintainer to request Swift package support or find an alternative package. + +We know that migrations can sometimes hit a snag. If SwiftPM causes a breaking issue, you can temporarily disable it for your project. Open your `pubspec.yaml` file, navigate to the `flutter` section, and set `enable-swift-package-manager` to false under the config block: + +```yaml +flutter: + config: + enable-swift-package-manager: false +``` + + +If you opt out, please file a bug report using the [Flutter GitHub issue template](https://github.com/flutter/flutter/issues/new/choose) and let us know! Include the error details, a list of your plugins and versions, and copies of your Xcode project files to help us resolve the issue before CocoaPods is completely removed. + +## Plugin developers + +For plugin authors who maintain an iOS or macOS plugin, *you must add Swift Package Manager support if you haven’t already*. So far, 61% of the top 100 iOS plugins have migrated. We need the remaining plugins on board so app developers aren’t stuck relying on a deprecated tool. To encourage adoption, packages without Swift Package Manager support now receive lower pub.dev scores until they migrate. + +To add this support, add a `Package.swift` file and move your source files to match the standard Swift package structure. If you already migrated your plugin during the 2025 pilot, you need to complete one new step: you must add FlutterFramework as a dependency in your `Package.swift` file. Read the [Flutter migration docs for plugin authors](https://docs.flutter.dev/packages-and-plugins/swift-package-manager/for-plugin-authors) for full instructions. + +Thank you for helping with this migration to Swift Package Manager. We hope this simplifies and improves your development experience on iOS and macOS! \ No newline at end of file diff --git a/sites/www/content/blog/sharing-flutters-2024-roadmap/images/18v__Z0fIVOBm4uxFnMLAVg.webp b/sites/www/content/blog/sharing-flutters-2024-roadmap/images/18v__Z0fIVOBm4uxFnMLAVg.webp new file mode 100644 index 0000000000..e58b2d036f Binary files /dev/null and b/sites/www/content/blog/sharing-flutters-2024-roadmap/images/18v__Z0fIVOBm4uxFnMLAVg.webp differ diff --git a/sites/www/content/blog/sharing-flutters-2024-roadmap/index.md b/sites/www/content/blog/sharing-flutters-2024-roadmap/index.md new file mode 100644 index 0000000000..994acb1f60 --- /dev/null +++ b/sites/www/content/blog/sharing-flutters-2024-roadmap/index.md @@ -0,0 +1,20 @@ +--- +title: "Sharing Flutter’s 2024 roadmap" +description: "As an open source project with a thriving community, we strive to be transparent about our plans, with everything from issues to design…" +publishDate: 2024-02-15 +author: mit-mit +image: images/18v__Z0fIVOBm4uxFnMLAVg.webp +category: announcements +layout: blog +--- + +As an open source project with a thriving community, we strive to be transparent about our plans, with everything from issues to design specifications being shared in the open. We’ve heard a great deal of interest in Flutter’s feature roadmap. These kinds of roadmaps can be challenging in terms of predictability, as those plans tend to shift and adapt throughout the year, but we still feel it’s important to share our overall plans, with the stated caveat: plans may change. + +We’ve been publishing our roadmaps [since 2020](https://github.com/flutter/flutter/wiki/%5BArchive%5D-Old-Roadmaps), and **today we’re sharing our[ 2024 roadmap](https://github.com/flutter/flutter/wiki/Roadmap)**. This is a natural continuation of previous years’ work, still working towards our long-term goal of creating *the most popular, fastest growing, and highest-productivity multi-platform UI framework for building rich app experiences*. + +<DashImage figure src="images/18v__Z0fIVOBm4uxFnMLAVg.webp" /> + + +Note that what we’re listing here is primarily content gathered from those of us who work on Flutter as employees of Google. By now non-Google contributors outnumber those employed by Google, so this is not an exhaustive list of all the new and exciting things that we hope will come to Flutter this year! + +We’re **immensely thankful** for the community and your continued support. We can’t wait to see what you’ll build! \ No newline at end of file diff --git a/sites/www/content/blog/slivers-demystified/images/1D0lutEyy9ouTE7TVgG4IXw.gif b/sites/www/content/blog/slivers-demystified/images/1D0lutEyy9ouTE7TVgG4IXw.gif new file mode 100644 index 0000000000..112b38210f Binary files /dev/null and b/sites/www/content/blog/slivers-demystified/images/1D0lutEyy9ouTE7TVgG4IXw.gif differ diff --git a/sites/www/content/blog/slivers-demystified/images/1MWOkQGMfHiH4qXuX1FXWCA.gif b/sites/www/content/blog/slivers-demystified/images/1MWOkQGMfHiH4qXuX1FXWCA.gif new file mode 100644 index 0000000000..009e9f878d Binary files /dev/null and b/sites/www/content/blog/slivers-demystified/images/1MWOkQGMfHiH4qXuX1FXWCA.gif differ diff --git a/sites/www/content/blog/slivers-demystified/images/1Oz9-FVqgyjDr_wnrbSQEGQ.gif b/sites/www/content/blog/slivers-demystified/images/1Oz9-FVqgyjDr_wnrbSQEGQ.gif new file mode 100644 index 0000000000..b972a59cc4 Binary files /dev/null and b/sites/www/content/blog/slivers-demystified/images/1Oz9-FVqgyjDr_wnrbSQEGQ.gif differ diff --git a/sites/www/content/blog/slivers-demystified/images/1WM9-Gu6XUz4CYCS2hXfqSA.webp b/sites/www/content/blog/slivers-demystified/images/1WM9-Gu6XUz4CYCS2hXfqSA.webp new file mode 100644 index 0000000000..a7569680f1 Binary files /dev/null and b/sites/www/content/blog/slivers-demystified/images/1WM9-Gu6XUz4CYCS2hXfqSA.webp differ diff --git a/sites/www/content/blog/slivers-demystified/images/1acfGrRUFKsC4z_0wHHt_Lg.gif b/sites/www/content/blog/slivers-demystified/images/1acfGrRUFKsC4z_0wHHt_Lg.gif new file mode 100644 index 0000000000..a2d3d89284 Binary files /dev/null and b/sites/www/content/blog/slivers-demystified/images/1acfGrRUFKsC4z_0wHHt_Lg.gif differ diff --git a/sites/www/content/blog/slivers-demystified/images/1g5kTqAzL6FTJKnFictwJ5w.gif b/sites/www/content/blog/slivers-demystified/images/1g5kTqAzL6FTJKnFictwJ5w.gif new file mode 100644 index 0000000000..0a155781b6 Binary files /dev/null and b/sites/www/content/blog/slivers-demystified/images/1g5kTqAzL6FTJKnFictwJ5w.gif differ diff --git a/sites/www/content/blog/slivers-demystified/images/1s9aYJJApIUVblNZxOWs8DQ.gif b/sites/www/content/blog/slivers-demystified/images/1s9aYJJApIUVblNZxOWs8DQ.gif new file mode 100644 index 0000000000..af8682b7dc Binary files /dev/null and b/sites/www/content/blog/slivers-demystified/images/1s9aYJJApIUVblNZxOWs8DQ.gif differ diff --git a/sites/www/content/blog/slivers-demystified/images/1uKP16BI3_8_a9Fk_Q2oNvA.webp b/sites/www/content/blog/slivers-demystified/images/1uKP16BI3_8_a9Fk_Q2oNvA.webp new file mode 100644 index 0000000000..4f652529c2 Binary files /dev/null and b/sites/www/content/blog/slivers-demystified/images/1uKP16BI3_8_a9Fk_Q2oNvA.webp differ diff --git a/sites/www/content/blog/slivers-demystified/images/1vkO1sczH3DPBGPlr3epW_Q.gif b/sites/www/content/blog/slivers-demystified/images/1vkO1sczH3DPBGPlr3epW_Q.gif new file mode 100644 index 0000000000..94a7248299 Binary files /dev/null and b/sites/www/content/blog/slivers-demystified/images/1vkO1sczH3DPBGPlr3epW_Q.gif differ diff --git a/sites/www/content/blog/slivers-demystified/index.md b/sites/www/content/blog/slivers-demystified/index.md new file mode 100644 index 0000000000..c5c9836f3a --- /dev/null +++ b/sites/www/content/blog/slivers-demystified/index.md @@ -0,0 +1,311 @@ +--- +title: "Slivers, Demystified" +description: "Or, how to do fancy scrolling techniques in your mobile app with Flutter" +publishDate: 2018-10-18 +author: efortuna +image: images/1acfGrRUFKsC4z_0wHHt_Lg.gif +category: tutorial +layout: blog +--- + +Hello, fearless Flutter fans! Today we’re going to delve into a highly specialized topic — something most app developers can blissfully ignore and create great looking apps without needing to know or understand. Usually if you need to scroll through something, [**ListView**](https://docs.flutter.io/flutter/widgets/ListView-class.html) and [**GridView**](https://docs.flutter.io/flutter/widgets/GridView-class.html) will do the job. Done and done. But, if you seek deeper knowledge and want to spice up your scrolling abilities: + +<DashImage figure src="images/1acfGrRUFKsC4z_0wHHt_Lg.gif" /> + + +<DashImage figure src="images/1MWOkQGMfHiH4qXuX1FXWCA.gif" alt="Two scenarios where you might want to use Slivers." caption="Two scenarios where you might want to use Slivers." /> + + +Read on… + +Or wait… if you hate reading, check out these two videos that quickly summarizes the ideas in this article: + +<YoutubeEmbed id="R9C5KMJKluE" title="SliverAppBar (Flutter Widget of the Week)" fullwidth="true"/> + + +<YoutubeEmbed id="ORiTTaVY6mM" title="SliverList & SliverGrid (Flutter Widget of the Week)" fullwidth="true"/> + + +What? You’re still here? Well in that case: + +## What’s a Sliver and Why do I want it? + +Around the web I see a lot of FOS. That’s *Fear Of Slivers* for those not In The Know. But a sliver is just a portion of a scrollable area. That’s it! Under the covers, *all* of the scrollable views you use, like ListView and GridView, are actually implemented using Slivers. You can kind of think of Slivers as a lower-level interface, providing finer-grained control on implementing scrollable area. Because slivers can lazily build each item just as it scrolls into view, slivers are particularly useful for efficiently scrolling through large numbers of children. + +You might want this additional control in scrolling behavior if you: + +* Want an app bar with nonstandard behavior (disappearing as you scroll, changing size or color as you scroll, etc). + +* Need to scroll a list of items and a grid of items all together as one unit. (Yes, you can do this with a table in a ListView but that is much less efficient, particularly if you have a large table.) + +* Do something weird like a collapsing list with headers (see gif on the right at the top of this page). + +<DashImage figure src="images/1D0lutEyy9ouTE7TVgG4IXw.gif" alt="Scenario where you might want to use slivers: scrolling a list of items and a grid of items all together as one unit." caption="Scenario where you might want to use slivers: scrolling a list of items and a grid of items all together as one unit." /> + + +## How do I use it? + +All of these sliver components go inside a [CustomScrollView](https://docs.flutter.io/flutter/widgets/CustomScrollView-class.html). The rest is up to you for how to combine your list of slivers to make your custom scrollable area. You can reinvent a ListView by putting a SliverList inside a CustomScrollView and set nothing else. + +## SliverList + +SliverList takes a delegate parameter which provides the items in the list as they scroll into view. You can specify the actual list of children with a SliverChildListDelegate Or build them lazily with a SliverChildBuilderDelegate. + +``` +// Explicit list of children. No efficiency savings here since the +// children are already constructed. + +SliverList( + delegate: SliverChildListDelegate( + [ + Container(color: Colors.red, height: 150.0), + Container(color: Colors.purple, height: 150.0), + Container(color: Colors.green, height: 150.0), + ], + ), +); + +// This builds an infinite scrollable list of differently colored +// Containers. + +SliverList( + delegate: SliverChildBuilderDelegate((BuildContext context, int index) { + // To convert this infinite list to a list with three items, + // uncomment the following line: + // if (index > 3) return null; + return Container(color: getRandomColor(), height: 150.0); + }, + // Or, uncomment the following line: + // childCount: 3, + ), +); +``` + + +## SliverGrid + +SliverGrids *also* can specify children with a delegate, or an explicit list, just like SliverList. But there’s some additional formatting for the *cross*-axis dimension on a grid. There are three ways to specify how you want your grid layout: + +1. Count constructor to *count* how many items are, in this case, in the horizontal axis: `SliverGrid.count(children: scrollItems, crossAxisCount: 4)` + +1. Extent constructor to specify the maximum width of items to determine how many should fit across a grid. This is most useful if the items in your grid vary in size, you can limit how large a space they should take up (in this case, horizontally). `SliverGrid.extent(children: scrollItems, maxCrossAxisExtent: 90.0) // 90 logical pixels` + +1. Default constructor, passing in an explicit gridDelegate parameter: + +``` +// Re-implementing the above SliverGrid.count example: +SliverGrid( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 4, + ), + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + return new Container( + color: randomColor(), + height: 150.0); + } +); +``` + + +<DashImage figure src="images/1uKP16BI3_8_a9Fk_Q2oNvA.webp" /> + + +<DashImage figure src="images/1WM9-Gu6XUz4CYCS2hXfqSA.webp" alt="Visual result of SliverGrid.count and SliverGrid.extent, respectively, from the above code snippets." caption="Visual result of SliverGrid.count and SliverGrid.extent, respectively, from the above code snippets." /> + + +## SliverAppBar + +Okay, okay, enough exposition. Here’s what I know you all have been waiting for. How do I make those gorgeous expanding and contracting app-bars? The secret is to set both the flexibleSpace parameter and the expandedHeight parameter. You can set both for a different height and appearance for your app bar when it is expanded to its full size vs the “compressed” version. + +<DashImage figure src="images/1Oz9-FVqgyjDr_wnrbSQEGQ.gif" /> + + +Here’s the code for the above example: + +``` +CustomScrollView( + slivers: <Widget>[ + SliverAppBar( + title: Text('SliverAppBar'), + backgroundColor: Colors.green, + expandedHeight: 200.0, + flexibleSpace: FlexibleSpaceBar( + background: Image.asset('assets/forest.jpg', fit: BoxFit.cover), + ), + ), + SliverFixedExtentList( + itemExtent: 150.0, + delegate: SliverChildListDelegate( + [ + Container(color: Colors.red), + Container(color: Colors.purple), + Container(color: Colors.green), + Container(color: Colors.orange), + Container(color: Colors.yellow), + Container(color: Colors.pink), + ], + ), + ), + ], +); +``` + + +There’s some additional customization you can add on SliverAppBar. You can set the floating parameter to true to make the app bar reappear when you scroll down, even if you haven’t reached the top of the list. + +<DashImage figure src="images/1s9aYJJApIUVblNZxOWs8DQ.gif" /> + + +If you add both the snap parameter with the floating parameter, you can make the app bar fully snap back into view when you scroll down. + +<DashImage figure src="images/1vkO1sczH3DPBGPlr3epW_Q.gif" /> + + +## Putting it all together: a collapsible scrolling list with SliverPersistentHeader + +I tried to imagine the most unusual scrolling behavior I could think of that still might be useful. I came up with this scrolling collapsible list: + +<DashImage figure src="images/1g5kTqAzL6FTJKnFictwJ5w.gif" /> + + +``` +import 'package:flutter/material.dart'; + +import 'dart:math' as math; + +void main() => runApp(MyApp()); + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar(title: Text('Collapsing List Demo')), + body: CollapsingList(), + ), + ); + } +} + +class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { + _SliverAppBarDelegate({ + @required this.minHeight, + @required this.maxHeight, + @required this.child, + }); + + final double minHeight; + final double maxHeight; + final Widget child; + + @override + double get minExtent => minHeight; + + @override + double get maxExtent => math.max(maxHeight, minHeight); + + @override + Widget build( + BuildContext context, + double shrinkOffset, + bool overlapsContent) + { + return new SizedBox.expand(child: child); + } + + @override + bool shouldRebuild(_SliverAppBarDelegate oldDelegate) { + return maxHeight != oldDelegate.maxHeight || + minHeight != oldDelegate.minHeight || + child != oldDelegate.child; + } +} + +class CollapsingList extends StatelessWidget { + SliverPersistentHeader makeHeader(String headerText) { + return SliverPersistentHeader( + pinned: true, + delegate: _SliverAppBarDelegate( + minHeight: 60.0, + maxHeight: 200.0, + child: Container( + color: Colors.lightBlue, child: Center(child: + Text(headerText))), + ), + ); + } + + @override + Widget build(BuildContext context) { + return CustomScrollView( + slivers: <Widget>[ + makeHeader('Header Section 1'), + SliverGrid.count( + crossAxisCount: 3, + children: [ + Container(color: Colors.red, height: 150.0), + Container(color: Colors.purple, height: 150.0), + Container(color: Colors.green, height: 150.0), + Container(color: Colors.orange, height: 150.0), + Container(color: Colors.yellow, height: 150.0), + Container(color: Colors.pink, height: 150.0), + Container(color: Colors.cyan, height: 150.0), + Container(color: Colors.indigo, height: 150.0), + Container(color: Colors.blue, height: 150.0), + ], + ), + makeHeader('Header Section 2'), + SliverFixedExtentList( + itemExtent: 150.0, + delegate: SliverChildListDelegate( + [ + Container(color: Colors.red), + Container(color: Colors.purple), + Container(color: Colors.green), + Container(color: Colors.orange), + Container(color: Colors.yellow), + ], + ), + ), + makeHeader('Header Section 3'), + SliverGrid( + gridDelegate: + new SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 200.0, + mainAxisSpacing: 10.0, + crossAxisSpacing: 10.0, + childAspectRatio: 4.0, + ), + delegate: new SliverChildBuilderDelegate( + (BuildContext context, int index) { + return new Container( + alignment: Alignment.center, + color: Colors.teal[100 * (index % 9)], + child: new Text('grid item $index'), + ); + }, + childCount: 20, + ), + ), + makeHeader('Header Section 4'), + // Yes, this could also be a SliverFixedExtentList. Writing + // this way just for an example of SliverList construction. + SliverList( + delegate: SliverChildListDelegate( + [ + Container(color: Colors.pink, height: 150.0), + Container(color: Colors.cyan, height: 150.0), + Container(color: Colors.indigo, height: 150.0), + Container(color: Colors.blue, height: 150.0), + ], + ), + ), + ], + ); + } +} +``` + + +The last step (exercise left to the reader) would be to add a GestureDetector so that tapping on one of the headers allows you to jump to that section in the list. Take your newfound knowledge of Slivers and apply it to GestureDetection to make a cool collapsing list! \ No newline at end of file diff --git a/sites/www/content/blog/some-options-for-deserializing-json-with-flutter/images/0-1m66Jxnec3s8Qnb.webp b/sites/www/content/blog/some-options-for-deserializing-json-with-flutter/images/0-1m66Jxnec3s8Qnb.webp new file mode 100644 index 0000000000..e2a37ac271 Binary files /dev/null and b/sites/www/content/blog/some-options-for-deserializing-json-with-flutter/images/0-1m66Jxnec3s8Qnb.webp differ diff --git a/sites/www/content/blog/some-options-for-deserializing-json-with-flutter/index.md b/sites/www/content/blog/some-options-for-deserializing-json-with-flutter/index.md new file mode 100644 index 0000000000..3cc88b598f --- /dev/null +++ b/sites/www/content/blog/some-options-for-deserializing-json-with-flutter/index.md @@ -0,0 +1,511 @@ +--- +title: "Some Options for Deserializing JSON with Flutter" +description: "At some point, most apps need to reach out and grab data from an online endpoint. Making an HTTPS get request to pull down a weather report…" +publishDate: 2018-08-02 +author: redbrogdon +category: tutorial +layout: blog +--- + +At some point, most apps need to reach out and grab data from an online endpoint. Making an HTTPS `get` request to pull down a weather report or the World Cup final score is fairly straightforward, thanks to Dart’s `http` package: + +```dart +import 'dart:async'; +import 'package:http/http.dart' as http; + +final response = await http.get(myEndpointUrl); +if (response.statusCode == 200) { + // use the data in response.body +} else { + // handle a failed request +} + +``` + +The data in `response.body` is likely a JSON string, and there’s some work to be done before it’s ready for a widget. First, you need to parse the string into a more manageable representation of JSON. Then you must convert that representation into a model or some other strongly typed variable, so you can use it effectively. + +Fortunately, the Dart team and community have been slinging JSON around for a while, and can offer solutions. I’ll cover three, in ascending order of complexity: handwritten constructors, `json_serializable`, and `built_value`. + +The calls to deserialize data with all three approaches are very similar. Handwritten constructors and `json_serializable` result in lines like this one: + +```dart +final myObject = SimpleObject.fromJson(json.decode(aJsonString)); +``` + +And a deserialization call for `built_value` looks like this: + +```dart +final myObject = serializers.deserializeWith( + SimpleObject.serializer, json.decode(aJsonString)); +``` + +The real difference is in how much code within that `SimpleObject` class is generated on your behalf, and what it’s doing. + +**Handwritten constructors** + +* Least complicated approach — no code is generated for you. + +* You can do whatever you want, but you’re on the hook for maintaining it. + +**`json_serializable`** + +* Generates a `fromJson` constructor and a `toJson` method for you. + +* You need to include several packages in your project and use `source_gen` to generate partial files prior to building your app. + +* Generated source can be tricky to customize. + +**`built_value`** + +* Generates code for serialization, immutability, `toString` methods, `hashCode` properties, and more. It’s a heavyweight solution with a lot of functionality. + +* Like `json_serializable`, you need to import a number of packages and use `source_gen`. + +* Has an extensible, plugin-based serialization architecture. + +* Has opinions about things like instance creation and mutability. + +As you’ll see, which library is right for you really depends on the details of your project, in particular its size and method of state management. A hobby project with one maintainer can go far with handwritten constructors, while an app built by a big, distributed team that needs immutable models to keep their logic clean can really benefit from `built_value`. + +For now, though, let’s start at the beginning: parsing JSON from a string into a more convenient in-memory representation. This step in the process is the same regardless of which way you decide to go later on. + +## Parsing JSON + +Converting a JSON string into an intermediate format can be done with the `dart:convert` library: + +```dart +import 'dart:convert'; + +try { + final parsed = json.decode(aJsonStr); +} on FormatException catch (e) { + print("That string didn't look like Json."); +} on NoSuchMethodError catch (e) { + print('That string was null!'); +} + +``` + +If the string contains valid JSON, you’ll get back a `dynamic` reference to either a `List<dynamic>` or a `Map<String, dynamic>` depending on whether the JSON string held an array or a single object. For simple things like a list of integers, you’re pretty much done at this point. You’ll probably want to create a second, strongly typed reference to the data, though, before you use it: + +```dart +final dynamicListOfInts = json.decode(aJsonArrayOfInts); + +// Create a strongly typed list with references to the data that are casted +// immediately. This is probably the better approach for data model classes. +final strongListOfInts = List<int>.from(dynamicListOfInts); + +// Or create a strongly typed list with references that will be lazy-casted +// when used. +final anotherStrongListOfInts = List<int>.from(dynamicListOfInts); +``` + +More sophisticated payloads are where things get interesting. Converting a `Map<String, dynamic>` into an actual model object can involve casting, default values, nulls, and nested objects. There are many ways things can go wrong, and many annoying details that need to be updated if you later decide to rename or add/remove a property. + +## Handwritten constructors + +One has to start somewhere, right? If you have a small app and the data is not that complicated, you can go a long way by writing your own [factory constructors](https://www.dartlang.org/guides/language/language-tour#factory-constructors) that take a `Map<String, dynamic>` parameter. For example, if you’re fetching that looks like this: + +```json +{ + "aString": "Blah, blah, blah.", + "anInt": 1, + "aDouble": 1.0, + "aListOfStrings": ["one", "two", "three"], + "aListOfInts": [1, 2, 3], + "aListOfDoubles": [1.0, 2.0, 3.0] +} + +``` + +The code for a matching class might look like this: + +```dart +class SimpleObject { + const SimpleObject({ + this.aString, + this.anInt, + this.aDouble, + this.aListOfStrings, + this.aListOfInts, + this.aListOfDoubles, + }); + + final String aString; + final int anInt; + final double aDouble; + final List<String> aListOfStrings; + final List<int> aListOfInts; + final List<double> aListOfDoubles; + + factory SimpleObject.fromJson(Map<String, dynamic> json) { + if (json == null) { + throw FormatException("Null JSON provided to SimpleObject"); + } + + return SimpleObject( + aString: json['aString'], + anInt: json['anInt'], + aDouble: json['aDouble'], + aListOfStrings: json['aListOfStrings'] != null + ? List<String>.from(json['aListOfStrings']) + : null, + aListOfInts: json['aListOfInts'] != null + ? List<int>.from(json['aListOfInts']) + : null, + aListOfDoubles: json['aListOfDoubles'] != null + ? List<double>.from(json['aListOfDoubles']) + : null, + ); + } +} + +``` + +The named `fromJson` factory constructor is then used like this: + +```dart +final myObject = SimpleObject.fromJson(json.decode(aJsonString)); +``` + +On the downside, you had to write ~20 lines of constructor code by hand, and you’re now on the hook for maintaining it. As your app grows and your data classes start to number in the dozens, you might find yourself thinking, “Man, coding these JSON constructors is getting tedious — I wish they could just be auto-generated from the properties in the class.” + +As it turns out, with the `json_serializable` library, they can. + +## Using json_serializable + +Before getting into `json_serializable`, we need to take a brief detour to talk about another package. + +Flutter doesn’t currently offer support for reflection, so some techniques available in other contexts (such as the ability of an Android JSON library to inspect classes for annotations at runtime) won’t work for Flutter devs. What they *can* use, however, is a Dart package called [`source_gen`](https://github.com/dart-lang/source_gen). It provides utilities and a basic framework for automated source code generation. + +Rather than updating your code directly, `source_gen` creates new, separate files alongside them. By convention, they’re given a “g” in their filenames, so if your class resides in `model.dart`, `source_gen` creates `model.g.dart`. You can reference that file in the original using the `part` keyword, and the compiler inlines it. + +The [`json_serializable`](https://pub.dartlang.org/packages/json_serializable) package uses the `source_gen` API to generate serialization code. It writes the `fromJson` constructor (as well as a `toJson` method) for you. + +The basic process for putting it to work in an app looks like this: + +1. [Import into your project](https://github.com/dart-lang/json_serializable/tree/master/example) the `json_serializable` and `json_annotation` packages. + +1. Define a data class as you normally would. + +1. Add the `@JsonSerializable` annotation to the class definition. + +1. Add some extra bits to link that class with the code `json_serializable` creates for it. + +1. Run `source_gen` to generate code. + +I’ll tackle these one at a time. + +### **Import the `json_serializable` package into your project** + +You can find `json_serializable` in the [Dart package catalog](https://pub.dartlang.org/packages/json_serializable). Just [update your `pubspec.yaml`](https://flutter.io/using-packages/#adding-a-package-dependency-to-an-app) as directed, and you’re good to go. + +### **Define a data class** + +No surprises here. Make a data class with basic properties and a constructor. The properties you plan to serialize should either be value types or other classes made to work with `json_serializable`. + +```dart +class SimpleObject { + SimpleObject({ + this.aString, + this.anInt, + this.aDouble, + this.aListOfStrings, + this.aListOfInts, + this.aListOfDoubles, + }); + + final String aString; + final int anInt; + final double aDouble; + final List<String> aListOfStrings; + final List<int> aListOfInts; + final List<double> aListOfDoubles; +} + +``` + +### **Add the @JsonSerializable annotation** + +The `json_serializable` package only generates code for classes that have been tagged with the `@JsonSerializable` annotation: + +```dart +import 'package:json_annotation/json_annotation.dart'; + +@JsonSerializable +class SimpleObject { + ... +} + +``` + +### **Link the generated code with yours** + +Next up are three changes that wire the class definition to its corresponding part file: + +```dart +import 'package:json_annotation/json_annotation.dart'; + +part 'simple_object.g.dart'; + +@JsonSerializable() +class SimpleObject extends Object with _$SimpleObjectSerializerMixin { + SimpleObject({ + this.aString, + this.anInt, + this.aDouble, + this.aListOfStrings, + this.aListOfInts, + this.aListOfDoubles, + }); + + final String aString; + final int anInt; + final double aDouble; + final List<String> aListOfStrings; + final List<int> aListOfInts; + final List<double> aListOfDoubles; + + factory SimpleObject.fromJson(Map<String, dynamic> json) => + _$SimpleObjectFromJson(json); +} + +``` + +The first of these is the `part` declaration, which tells the compiler to inline `simple_object.g.dart` (more on that in a minute). Then there’s an update to the class definition to use a mixin. Finally, update the class to use the `fromJson` constructor. The last two changes each reference code in the generated file. + +### **Run source_gen** + +Kick off code generation from your project folder with this command: + +``` +flutter packages pub run build_runner build +``` + + +When finished, you’ll have a new file called `simple_object.g.dart` sitting alongside the original. The contents look like this: + +```dart +part of 'simple_object.dart'; + +SimpleObject _$SimpleObjectFromJson( + Map<String, dynamic> json) => + new SimpleObject( + aString: json['aString'] as String, + anInt: json['anInt'] as int, + aDouble: (json['aDouble'] as num)?.toDouble(), + aListOfStrings: + (json['aListOfStrings'] as List)?.map((e) => e as String)?.toList(), + aListOfInts: + (json['aListOfInts'] as List)?.map((e) => e as int)?.toList(), + aListOfDoubles: (json['aListOfDoubles'] as List) + ?.map((e) => (e as num)?.toDouble()) + ?.toList()); + +abstract class _$SimpleObjectSerializerMixin { + String get aString; + int get anInt; + double get aDouble; + List<String> get aListOfStrings; + List<int> get aListOfInts; + List<double> get aListOfDoubles; + Map<String, dynamic> toJson() => <String, dynamic>{ + 'aString': aString, + 'anInt': anInt, + 'aDouble': aDouble, + 'aListOfStrings': aListOfStrings, + 'aListOfInts': aListOfInts, + 'aListOfDoubles': aListOfDoubles + }; +} + +``` + +That first method is called by the `fromJson` constructor in `SimpleObject` and the mixin class provides the new `toJson` method to `SimpleObject`. Both are straightforward to use: + +```dart +final myObject = SimpleObject.fromJson(json.decode(jsonString)); +final myJsonStr = json.encode(myObject.toJson()); +``` + +In terms of numbers, adding three lines of code to `simple_object.dart` for `json_serializable` saves about 20 lines of constructor code that you’d otherwise have to write. You’re also able to regenerate it anytime you want to rename or otherwise adjust the properties, and you get a `toJson` method thrown in for free. Not too shabby. + +What if you’d like to serialize to more than one format, though? Or to more than just JSON? And what if you need other things, like immutable model objects? For those use cases, there’s `built_value`. + +## Using `built_value` + +Much more than just a solution for automating serialization logic, `built_value` (along with its partner package, `built_collection`) is designed to help you create classes that function as value types. For this reason, instances of classes created with `built_value` are immutable. You can create new instances (including copies of existing ones), but once they’re built, their properties can’t be changed. + +To accomplish this, `built_value` uses the same source generation approach found in `json_serializable`, but creates a lot more code. In the generated file for a `built_value` class, you’ll find: + +* An equality (`==`) operator + +* A `hashCode` property + +* A `toString` method + +* A serializer class, if you want one — more on that below + +* A “builder” class used to make new instances + +This adds up to several hundred lines, even for a small class like SimpleObject, so I’ll avoid showing it here. The actual class file (what you’d write as a dev) looks like this: + +```dart +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:built_value/serializer.dart'; + +part 'simple_object.g.dart'; + +abstract class SimpleObject + implements Built<SimpleObject, SimpleObjectBuilder> { + static Serializer<SimpleObject> get serializer => + _$SimpleObjectSerializer; + + @nullable + String get aString; + + @nullable + int get anInt; + + @nullable + double get aDouble; + + @nullable + BuiltList<String> get aListOfStrings; + + @nullable + BuiltList<int> get aListOfInts; + + @nullable + BuiltList<double> get aListOfDoubles; + + SimpleObject._(); + + factory SimpleObject([updates(SimpleObjectBuilder b)]) = + _$SimpleObject; +} + +``` + +The differences between this and the version of `SimpleObject` we started with are: + +* A part file is declared, just like `json_serializable`. + +* An interface, `Built<SimpleObject, SimpleObjectBuilder>`, is being implemented. + +* A static getter for a serializer object has been added. + +* Nullability annotations are on all the fields. These are optional, but to make this example match the others in functionality, I’ve added them. + +* Two constructors (one private, one factory) have been added, and the original one was removed. + +* SimpleObject is now an abstract class! + +The differences between this and the version of `SimpleObject` we started with are: + +Let’s start with the last point: `SimpleObject` has become an abstract class. In its generated file, `built_value` defines a subclass of `SimpleObject` called `_$SimpleObject`, and that’s where it provides a lot of the new functionality. It’s where you’ll find the new `hashCode` property, new methods relating to immutability, and so on. Each time you create an instance of `SimpleObject`, you’re actually getting `_$SimpleObject` under the hood. You’ll never need to reference it by the derived type, though, so your app code still uses `SimpleObject` to declare and use references. + +This is possible because instantiation of a brand-new `SimpleObject` is done through a generated factory constructor, which you can see referenced in the last line of the file above. To use it, you pass in a method that sets properties on a `SimpleObjectBuilder` (it’s the “b” parameter below), which builds the immutable object instance for you: + +```dart +final SimpleObject myObject = SimpleObject((b) => b + ..aString = 'Blah, blah, blah' + ..anInt = 1 + ..aDouble = 1.0 + ..aListOfStrings = ListBuilder<String>(['one', 'two', 'three']) + ..aListOfInts = ListBuilder<int>([1, 2, 3]) + ..aListOfDoubles = ListBuilder<double>([1.0, 2.0, 3.0]) +); + +``` + +You can also `rebuild` to get a modified bopy of an existing instance: + +```dart +final SimpleObject anotherObject = myObject.rebuild((b) => b + ..aString = "An updated value" +); + +``` + +As you can see, the constructor in `SimpleObject` has been made private through the use of an underscore: + +```dart +SimpleObject._(); +``` + +That guarantees that your app’s code can’t directly instantiate an instance of `SimpleObject`. In order to get one, you have to use the factory constructor, which uses `SimpleObjectBuilder` and always produces an instance of the `_$SimpleObject` subclass. + +### **That’s great, but I thought we were talking about deserialization?** + +I’m getting to that! To serialize and deserialize instances, you’ll need to add a little code somewhere in the app (creating a file called `serializers.dart`, for example, is a good approach): + +```dart +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/serializer.dart'; +import 'package:built_value/standard_json_plugin.dart'; +import 'simple_object.dart'; + +part 'serializers.g.dart'; + +@SerializersFor(const [ + SimpleObject, +]) + +final Serializers serializers = + (_$serializers.toBuilder()..addPlugin(StandardJsonPlugin())).build(); +``` + +This file does two things. First, it uses the `@SerializersFor` annotation to instruct `built_value` to create serializers for a list of classes. In this case, there’s only one class, so it’s a short list. Second, it creates a global variable called `serializers` that references the `Serializers` object that handles serialization of `built_value` classes. It’s used like this: + +```dart +final myObject = serializers.deserializeWith( + SimpleObject.serializer, json.decode(aJsonString)); + +final String myJsonString = json.encode(serializers.serialize(myObject)); +``` + +As with `json_serializable`, transforming an object into and out of JSON is still mostly a one line affair, with the generated code doing the heavy lifting for you. One important thing to note, though, is this bit from `serializers.dart`: + +```dart +final Serializers serializers = + (_$serializers.toBuilder()..addPlugin(StandardJsonPlugin())).build(); +``` + +`built_value` is designed to be as extensible as possible, and it includes a plugin system for defining custom serialization formats (you could, for example, write one to translate to and from XML or your own binary format). I’m using it in this example to add a plugin called `StandardJsonPlugin` because, by default, `built_value` d*oesn’t use the map-based JSON format that you’re probably used to*. + +Instead, it uses a list-based format. For example, a simple object with `String`, `int`, and `double` members would be serialized like this: + +```json +[ + "SimpleObject", + "aString", + "Blah, blah, blah", + "anInt", + 1, + "aDouble", + 2.0 +] + +``` + +Rather than this: + +```json +{ + "$": "SimpleObject", + "aString": "Blah, blah, blah", + "anInt": 1, + "aDouble": 2.0 +} + +``` + +There are a few reasons why `built_value` prefers the list-based format, which for sake of space I’ll leave to the [package documentation](https://github.com/google/built_value.dart). For this example, though, just know that you can easily make use of map-based JSON serialization through the `StandardJsonPlugin`, which ships as part of the `built_value` package. + +## Conclusion + +So those are the high points of all three techniques. As I mentioned back at the beginning of this article, choosing the right one is mostly about considering the scope of your project, how many people are working on it, and what other needs you have for your model objects. + +The next step is to start coding, so come see us at the [Flutter Dev Google Group](https://groups.google.com/forum/#!forum/flutter-dev), [StackOverflow](https://stackoverflow.com/questions/tagged/flutter), or [The Boring Show](https://www.youtube.com/watch?v=TiCA0CEePyE&list=PLOU2XLYxmsIK0r_D-zWcmJ1plIcDNnRkK&index=2), and let us know how it goes! \ No newline at end of file diff --git a/sites/www/content/blog/starting-2024-strong-with-flutter-and-dart/images/02UG76__vsbp6NHtN.webp b/sites/www/content/blog/starting-2024-strong-with-flutter-and-dart/images/02UG76__vsbp6NHtN.webp new file mode 100644 index 0000000000..22bc6777cf Binary files /dev/null and b/sites/www/content/blog/starting-2024-strong-with-flutter-and-dart/images/02UG76__vsbp6NHtN.webp differ diff --git a/sites/www/content/blog/starting-2024-strong-with-flutter-and-dart/images/05hGoIuMtVnvrZ2id.webp b/sites/www/content/blog/starting-2024-strong-with-flutter-and-dart/images/05hGoIuMtVnvrZ2id.webp new file mode 100644 index 0000000000..758aa7d5c3 Binary files /dev/null and b/sites/www/content/blog/starting-2024-strong-with-flutter-and-dart/images/05hGoIuMtVnvrZ2id.webp differ diff --git a/sites/www/content/blog/starting-2024-strong-with-flutter-and-dart/images/0gO0yYMh4FUkpue3w.gif b/sites/www/content/blog/starting-2024-strong-with-flutter-and-dart/images/0gO0yYMh4FUkpue3w.gif new file mode 100644 index 0000000000..87152164c6 Binary files /dev/null and b/sites/www/content/blog/starting-2024-strong-with-flutter-and-dart/images/0gO0yYMh4FUkpue3w.gif differ diff --git a/sites/www/content/blog/starting-2024-strong-with-flutter-and-dart/images/0oAoUwrRrrYWIbu_u.webp b/sites/www/content/blog/starting-2024-strong-with-flutter-and-dart/images/0oAoUwrRrrYWIbu_u.webp new file mode 100644 index 0000000000..e58b2d036f Binary files /dev/null and b/sites/www/content/blog/starting-2024-strong-with-flutter-and-dart/images/0oAoUwrRrrYWIbu_u.webp differ diff --git a/sites/www/content/blog/starting-2024-strong-with-flutter-and-dart/index.md b/sites/www/content/blog/starting-2024-strong-with-flutter-and-dart/index.md new file mode 100644 index 0000000000..2061d744c8 --- /dev/null +++ b/sites/www/content/blog/starting-2024-strong-with-flutter-and-dart/index.md @@ -0,0 +1,88 @@ +--- +title: "Starting 2024 strong with Flutter and Dart" +description: "An introduction, two new SDK releases, and bringing Flutter and Dart into the Gemini era" +publishDate: 2024-02-15 +author: brandonbadger +image: images/0gO0yYMh4FUkpue3w.gif +category: announcements +layout: blog +--- + +*We’re excited to announce the first of this year’s quarterly SDK releases for Flutter and Dart — Flutter 3.19 and Dart 3.3, along with some exciting announcements involving AI.* + +<DashImage figure src="images/0gO0yYMh4FUkpue3w.gif" /> + + +**Flutter and Dart’s value and future** + +I recently joined the Flutter and Dart team, and am excited to work with our developer community at a time when AI is moving quickly to enhance developer productivity and unlock new types of user experiences. I see endless potential in Flutter and Dart’s role in helping to shape this future. I’m equally inspired by the original vision for Flutter to improve the experience of building beautiful, performant, multi platform apps for any device***.*** + +Judging by the millions of talented and creative developers who support Flutter with contributions to the framework, or by building amazing experiences — it’s clear others see this vision and are motivated to help. And the core mission remains the same: to deliver a strong language and framework pairing, enabling creative developers to build beautiful, rich, and performant apps for any device. Let’s do this together! + +**Bringing Flutter and Dart into the Gemini era** + +Today we launched the [Google AI Dart SDK](https://medium.com/flutter/harness-gemini-in-your-dart-and-flutter-apps-00573e560381), a new pub.dev package, [google_generative_ai,](https://pub.dev/packages/google_generative_ai) and [supporting resources](https://ai.google.dev/tutorials/dart_quickstart); together these enable you to build your own generative AI-based features like smart chat bots, visual search engines, and image descriptions into Dart and Flutter apps using the Gemini API. Flutter and Dart’s cross-platform capabilities and this new SDK make it easier for you to build interactive experiences across platforms. + +<DashImage figure src="images/05hGoIuMtVnvrZ2id.webp" /> + + +And this is only the beginning of the innovations that we’re bringing to Flutter and Dart development with AI. For instance, soon Flutter and Dart developers can copy Dart code directly from [Google’s AI Studio](https://aistudio.google.com/?utm_source=flutter&utm_medium=referral&utm_campaign=blog_umbrella_announcement&utm_content=) after honing prompts for your use case. + +Learn more about the Google AI Dart SDK in the [deep dive blog post](https://medium.com/flutter/harness-gemini-in-your-dart-and-flutter-apps-00573e560381). + +Many developers have already begun to bring Flutter and AI tools together in exciting ways: + +* The team at [LeanCode](https://leancode.co/) have used the Gemini model to build [arb_translate](https://leancode.co/arb_translate), a package that allows developers to perform translation tasks automatically + +* We Spot Turtles! have combined Flutter and AI in their mission to save sea turtles from extinction. They were recently featured in Google Play’s [#WeArePlay campaign](https://play.google.com/console/about/weareplay/). Take a look below. + +* AutoGPT, an experimental, open-source project that builds on top of large language models (LLMs), has a [Flutter client](https://github.com/Significant-Gravitas/auto_gpt_flutter_client) that runs across iOS, Android, web, macOS, and Windows. + +[#WeArePlay | Caitlin and Nicolas | We Spot Turtles! | Australia](https://youtu.be/CfzhLOiczDQ?si=Qgc4Yb4Q9xKI6byF) + +<YoutubeEmbed id="CfzhLOiczDQ" title="#WeArePlay | Caitlin and Nicolas | We Spot Turtles! | Australia" fullwidth="true"/> + + +As you explore the Gemini model’s capabilities, be sure to share the new and innovate experiences you’re building with us using the #BuildWithGemini hashtag. + +**Two new SDK releases** + +Aside from the excitement that AI brings, we remain focused on continuing to build a strong UI framework, capable of delivering any experience you want to build on any screen you want to build for. You’ll see progress towards that vision in today’s SDK releases, Flutter 3.19 and Dart 3.3. + +These releases are focused on refinements and performance improvements that build upon the trajectory that Flutter and Dart set out [last year](https://medium.com/flutter/whats-next-for-flutter-b94ce089f49c). In this Flutter release, you’ll find: + +* Updates to our work to generate breakthrough graphics performance through our continued work on Impeller + +* Additional steps towards providing seamless integration between platforms with Flutter iOS native fonts and an early version of a deep linking web validator + +* Continued focus on the developer experience with updates to DevTools and a [progress report on the Flutter package ecosystem](https://medium.com/flutter/progress-of-the-flutter-package-ecosystem-17cded9a0703?source=collection_home---4------1-----------------------) + +* And finally, we’re excited to share progress on our mission to help define the future of the web with Wasm + +<DashImage figure src="images/02UG76__vsbp6NHtN.webp" /> + + +Dart 3.3 in turn, introduces extension types, a new model for interacting with JavaScript on the web, and updates to our work to support access to more and better web libraries. You can learn more about each release in the blog posts for [Flutter 3.19](https://medium.com/flutter/whats-new-in-flutter-3-19-58b1aae242d2) and [Dart 3.3](https://medium.com/dartlang/dart-3-3-325bf2bf6c13), respectively. + +**2024 Strategy and Roadmap** + +Each of these features is a small step in a larger journey we’re taking this year, and that you can see in our [2024 roadmap](https://github.com/flutter/flutter/wiki/Roadmap). As always, these roadmaps are born from a desire to be open about our plans as we know many of you consider Flutter and Dart to be essential components in your careers and businesses. That being said, progress can be difficult to predict, even with a plan in place. + +And while we’ll do our best to continue to remain transparent as changes inevitably force us to shift focus and make tradeoffs, we want to highlight that there are more contributors to Flutter and Dart outside of Google than those of us employed here, meaning that the things mentioned in our roadmap are but a small portion of the thousands of changes that will come to Dart and Flutter this year. + +<DashImage figure src="images/0oAoUwrRrrYWIbu_u.webp" alt="2024 roadmap" caption="2024 roadmap" /> + + +**Bringing it all together** + +I’ve worked on many fun and innovative projects during my 17+ years at Google and YouTube, but this is the most enthusiastic I’ve felt about leaning into a new opportunity. I started my career as a software engineer, and my first job at Google was as the PM for the Maps API and Geo developer tools, so it’s great to get back to my developer roots. + +Part of what drives my enthusiasm is, well, your enthusiasm. Just in 2024 so far, I’ve seen such amazing things come out of this community, including: + +* Over 2,700 of you have joined our[ Global Gamers Challenge](http://flutter.dev/global-gamers), which we’re hosting in partnership with international advocacy firm Global Citizen to challenge you to use your skills to build Flutter games that inspire the world to live more sustainably. + +* [Superlist](https://youtu.be/37qvcjmE51w), who announced version 1.0 yesterday, is using Flutter to redefine task management, note taking, and everything in between. + +In closing, my kids are starting to study Computer Science, and I’m motivated to help create the software development experience that will help the next generation change the world for the better. + +This year promises to be an important one for creating that future, and I can’t wait to see the ways in which Flutter and Dart support it. As always, we’re incredibly grateful for your continued support and we can’t wait to see what we’ll build together. Until next time! \ No newline at end of file diff --git a/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/0YwvNR6rKmm9AZd8-.webp b/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/0YwvNR6rKmm9AZd8-.webp new file mode 100644 index 0000000000..4b8a47ebb6 Binary files /dev/null and b/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/0YwvNR6rKmm9AZd8-.webp differ diff --git a/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/0_waE6swxNd-8j2r-.webp b/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/0_waE6swxNd-8j2r-.webp new file mode 100644 index 0000000000..23adac74b6 Binary files /dev/null and b/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/0_waE6swxNd-8j2r-.webp differ diff --git a/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/0cmGZIWZvGJlGLslQ.webp b/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/0cmGZIWZvGJlGLslQ.webp new file mode 100644 index 0000000000..9854ffecd8 Binary files /dev/null and b/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/0cmGZIWZvGJlGLslQ.webp differ diff --git a/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/0ejP8ZefmPBh8PF1o.webp b/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/0ejP8ZefmPBh8PF1o.webp new file mode 100644 index 0000000000..e771dc0cf0 Binary files /dev/null and b/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/0ejP8ZefmPBh8PF1o.webp differ diff --git a/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/0fSlfAxkzo4DXfFvW.webp b/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/0fSlfAxkzo4DXfFvW.webp new file mode 100644 index 0000000000..b88cf218bd Binary files /dev/null and b/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/0fSlfAxkzo4DXfFvW.webp differ diff --git a/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/0lsCeAx1gT5qxWONz.webp b/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/0lsCeAx1gT5qxWONz.webp new file mode 100644 index 0000000000..17287025e3 Binary files /dev/null and b/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/0lsCeAx1gT5qxWONz.webp differ diff --git a/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/1lHHYRiiookkYcA-Uhq9pgg.webp b/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/1lHHYRiiookkYcA-Uhq9pgg.webp new file mode 100644 index 0000000000..1c3d79b5bc Binary files /dev/null and b/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/images/1lHHYRiiookkYcA-Uhq9pgg.webp differ diff --git a/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/index.md b/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/index.md new file mode 100644 index 0000000000..d45214fab9 --- /dev/null +++ b/sites/www/content/blog/studying-developers-usage-of-ides-for-flutter-development/index.md @@ -0,0 +1,76 @@ +--- +title: "Studying developer’s usage of IDEs for Flutter development" +description: "Findings from the Q2 2022 user survey. Written by JaYoung Lee and Ander Dobo." +publishDate: 2022-09-12 +author: jayoung-lee +image: images/1lHHYRiiookkYcA-Uhq9pgg.webp +category: announcements +layout: blog +--- + +## Studying developer’s IDE usage for Flutter development + +The Flutter team at Google currently builds and maintains support for Android Studio (which is based on IntelliJ-IDEA), and Visual Studio Code (VS Code). We integrate features into plugins for these IDEs for Flutter app development, such as code completion, syntax highlighting, widget editing assists, run & debug support, and more. Flutter developers have used both IDEs since the earliest days of Flutter with Android Studio, traditionally being more popular than VS Code. VS Code has steadily gained popularity faster than, and recently surpassed, the popularity of Android Studio for Flutter development as shown in the figure below. + +To better understand Flutter developers’ thoughts behind IDE choice, the Flutter UX team fielded a survey specifically for this topic in May 2022. + +*In the rest of the article, “Android Studio” represents both Android Studio and IntelliJ-IDEA together.* + +<DashImage figure src="images/1lHHYRiiookkYcA-Uhq9pgg.webp" alt="*Figure 1. The number of Flutter developers from each IDE, from August 2021 to July 2022, shows that VS Code became more popular in the recent few months.*" caption="*Figure 1. The number of Flutter developers from each IDE, from August 2021 to July 2022, shows that VS Code became more popular in the recent few months.*" /> + + +## What we already knew about the IDEs + +In addition to the fact that Flutter developers were split between Android Studio and VS Code 50:50 at the beginning of 2022, we knew a bit more about the developers’ choice based on the previous surveys. + +### Flutter developers tend to use the IDE familiar to them + +In 2019 Q3, we asked Flutter developers why they prefer the IDE they use the most. The most common choice was that the IDE was “more familiar to me”. + +<DashImage figure src="images/0fSlfAxkzo4DXfFvW.webp" alt="*Figure 2. 80% of Android Studio users and 61% of VS Code users said that they chose the IDE because of familiarity, according to the Q3 2019 survey.*" caption="*Figure 2. 80% of Android Studio users and 61% of VS Code users said that they chose the IDE because of familiarity, according to the Q3 2019 survey.*" /> + + +### VS Code users value the speed they experience with the IDE + +Another notable thing from the figure above (Figure 2) is that 68% of VS Code users chose the IDE because it’s faster than other IDEs, while only 12% of Android Studio users thought so. In an open-ended question, VS Code users said that they like the IDE because it is lightweight but has a variety of extensions. + +### VS Code users are more satisfied with Flutter’s IDE support + +We also asked about satisfaction with Flutter’s IDE support, and VS Code users tended to be more satisfied. (We record the IDE developers are using when they open the survey from an IDE. We notify developers about this recording when they click the survey link.) + +<DashImage figure src="images/0cmGZIWZvGJlGLslQ.webp" alt="*Figure 3. While 93.3% of VS Code users are satisfied with Flutter’s IDE support, only 85.9% of Android Studio users are satisfied.*" caption="*Figure 3. While 93.3% of VS Code users are satisfied with Flutter’s IDE support, only 85.9% of Android Studio users are satisfied.*" /> + + +Android Studio is, of course, designed as a fully-integrated IDE for Android development, so it has a much richer feature set for that purpose. Developers have mentioned that it’s easy to handle native Android code and use convenient features like refactoring in Android Studio. In the next section, we dig deeper into the preference and why developers are less happy with using Flutter in Android Studio despite these advantages. + +## What we learned from the Q2 2022 survey + +The results above made us wonder why Flutter developers are happier with the VS Code support than with the Android Studio support. We wanted to understand the things that Flutter developers really like about VS Code. + +To understand that, we asked a set of questions to developers who switched their main IDE from one to the other in either direction. We believe these developers are well placed to tell us about the unique values in each IDE from their perspective. + +First of all, there were more Flutter developers who switched from Android Studio to VS Code. + +<DashImage figure src="images/0YwvNR6rKmm9AZd8-.webp" alt="*Figure 4. More Flutter developers switched their main IDE from Android Studio (blue) to VS Code (teal) than vice versa.*" caption="*Figure 4. More Flutter developers switched their main IDE from Android Studio (blue) to VS Code (teal) than vice versa.*" /> + + +As shown in the following graph, those who switched to VS Code liked its performance (82%) and usability (63%). On the other hand, those who switched to Android Studio liked its features (51%) and integration with Flutter tools (39%) and native platforms (27%). + +<DashImage figure src="images/0lsCeAx1gT5qxWONz.webp" alt="*Figure 5. Reasons for switching to a new IDE.*" caption="*Figure 5. Reasons for switching to a new IDE.*" /> + + +Still, about 23% of Flutter developers use *both* VS Code and Android Studio. When we asked them why they use more than one IDE, the most prominent theme that emerged was that VS Code users need to use Android Studio and Xcode for native-specific functionalities, such as emulator set-up, build configuration, release requirements (such as key generation and signing), and when developing hybrid Flutter+native apps. + +<DashImage figure src="images/0_waE6swxNd-8j2r-.webp" alt="Figure 6. 22.5% of Flutter developers use both VS Code and Android Studio together" caption="Figure 6. 22.5% of Flutter developers use both VS Code and Android Studio together" /> + + +Another insight we gained from the survey is that Flutter developers from different countries or regions prefer different IDEs. While most Flutter developers prefer VS Code over Android Studio, developers from China prefer Android Studio (56%) over VSCode (23%). We found this interesting — Flutter is adopted globally, yet used under different development environments. Please leave a comment on this article, if you have any stories from your country or region to share around this tendency. + +<DashImage figure src="images/0ejP8ZefmPBh8PF1o.webp" alt="Figure 7. IDE by countries or regions. Countries or regions with at least 100 survey respondents are included in the chart." caption="Figure 7. IDE by countries or regions. Countries or regions with at least 100 survey respondents are included in the chart." /> + + +## In conclusion + +Our goal is to deliver a useful and complete development experience that minimizes the friction to get started with Flutter and maximizes developers’ productivity. We will take account of these and future study findings to inform our roadmap for future Flutter IDE support and documentation. As a first step, we’re making minor updates to our website’s getting started documentation to reflect the IDE preferences and patterns of usage described above. + +The insights we gain from this and other surveys are invaluable to us and ensure that we invest in the right areas to continue improving the Flutter developer experience. Thank you to everyone who participated! If you’re interested in participating in future user studies, you can sign up at [https://flutter.dev/research-signup](https://flutter.dev/research-signup). We’ll be back next quarter to share the Q3 survey results. See you in the next article! \ No newline at end of file diff --git a/sites/www/content/blog/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server/images/0Vjo7hhpe6uDe05yS.webp b/sites/www/content/blog/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server/images/0Vjo7hhpe6uDe05yS.webp new file mode 100644 index 0000000000..05cdd4145d Binary files /dev/null and b/sites/www/content/blog/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server/images/0Vjo7hhpe6uDe05yS.webp differ diff --git a/sites/www/content/blog/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server/images/0gD-O6VeS0xT6EdDh.webp b/sites/www/content/blog/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server/images/0gD-O6VeS0xT6EdDh.webp new file mode 100644 index 0000000000..e676ab95b5 Binary files /dev/null and b/sites/www/content/blog/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server/images/0gD-O6VeS0xT6EdDh.webp differ diff --git a/sites/www/content/blog/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server/images/13K1rgoDtIBWbIFZPp1VCEQ.webp b/sites/www/content/blog/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server/images/13K1rgoDtIBWbIFZPp1VCEQ.webp new file mode 100644 index 0000000000..707f45787e Binary files /dev/null and b/sites/www/content/blog/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server/images/13K1rgoDtIBWbIFZPp1VCEQ.webp differ diff --git a/sites/www/content/blog/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server/images/1DGTEKfS3-ZANFr66VG8MRw.gif b/sites/www/content/blog/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server/images/1DGTEKfS3-ZANFr66VG8MRw.gif new file mode 100644 index 0000000000..f85071ec83 Binary files /dev/null and b/sites/www/content/blog/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server/images/1DGTEKfS3-ZANFr66VG8MRw.gif differ diff --git a/sites/www/content/blog/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server/images/1eYea3SzwoGDCQeP5Tmq-Pg.webp b/sites/www/content/blog/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server/images/1eYea3SzwoGDCQeP5Tmq-Pg.webp new file mode 100644 index 0000000000..7c6a3fd100 Binary files /dev/null and b/sites/www/content/blog/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server/images/1eYea3SzwoGDCQeP5Tmq-Pg.webp differ diff --git a/sites/www/content/blog/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server/index.md b/sites/www/content/blog/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server/index.md new file mode 100644 index 0000000000..be9f1c7b35 --- /dev/null +++ b/sites/www/content/blog/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server/index.md @@ -0,0 +1,226 @@ +--- +title: "Supercharge Your Dart & Flutter Development Experience with the Dart MCP Server" +description: "By Ander Dobo — Product Manager, and Kenzie Davisson — Engineer" +publishDate: 2025-07-23 +author: anderdobo +image: images/13K1rgoDtIBWbIFZPp1VCEQ.webp +category: announcements +layout: blog +--- + +## Supercharge Your Dart & Flutter Development Experience with the Dart and Flutter MCP Server + +**By Ander Dobo — Product Manager, and Kenzie Davisson — Engineer** + +The landscape of AI development is rapidly evolving, with powerful agents capable of assisting developers across a myriad of tasks. To truly unlock their potential, these agents need to access context and tools beyond their inherent capabilities. This is where the [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) comes in, acting as a standardized plugin system that connects AI models to external systems and data sources. The Dart and Flutter MCP Server is our implementation of this standard for the Dart and Flutter ecosystem. + +This article explores how you can leverage the Dart and Flutter MCP Server to enhance your Dart and Flutter development experience. We’ll cover useful tools provided by the Dart and Flutter MCP Server and show how you can configure the server for use in popular tools like the Gemini CLI, Gemini Code Assist in VS Code, Cursor, and GitHub Copilot in VS Code. + +This approach is part of a rapidly growing ecosystem. For instance, the Firebase team recently announced their own experimental [Firebase MCP Server](https://firebase.blog/posts/2025/05/firebase-mcp-server/), which gives AI assistants tools to manage Firebase Authentication, query a Firestore database, and understand the types of application crashes impacting your application in production. + +## Introducing the Dart and Flutter MCP Server + +The [Dart and Flutter MCP Server](https://github.com/dart-lang/ai/tree/main/pkgs/dart_mcp_server) (which requires the Dart SDK 3.9/Flutter 3.35 beta or later) is a powerful tool that bridges the gap between AI coding assistants and the Dart/Flutter development ecosystem. It allows AI models to interact with your development environment and your running Dart or Flutter application, providing intelligent insights, and using tooling normally only accessible from your IDE or from Dart & Flutter DevTools. + +## What can the Dart and Flutter MCP Server do for you? + +The Dart and Flutter MCP Server provides a growing list of tools that grant AI assistants deep insights into your project. It can: + +* **Analyze and fix errors** in your project’s code. + +* **Introspect and interact with your running application** (such as trigger a hot reload, get the selected widget, fetch runtime errors). + +* **Search pub.dev** for the best package for your use case. + +* **Manage package dependencies** in your `pubspec.yaml`. + +* **Run tests** and analyze the results. + +* …and more! + +See the [official documentation](https://dart.dev/tools/mcp-server) for the most up-to-date list of tools. + +## The Dart and Flutter MCP Server in action + +The true power of the Dart and Flutter MCP Server is how it enables AI assistants and agents to not only reason about your project’s context, but take action with tools. The Large Language Model (LLM) decides what tools to use and when, so you can focus on describing your goal in natural language. Let’s see this in action with a couple of examples using GitHub Copilot’s Agent mode in VS Code. + +### Fixing a runtime layout error + +We’ve all been there: you build a beautiful UI, run the app, and are greeted by the infamous yellow-and-black stripes of a `RenderFlex` overflow error. Instead of manually debugging the widget tree, you can now ask your AI assistant for help. + +<DashImage figure src="images/13K1rgoDtIBWbIFZPp1VCEQ.webp" /> + + +**Prompt:** *“Check for and fix static and runtime analysis issues. Check for and fix any layout issues.” (Note: For brevity, parts of this recording have been sped up.)* + +Behind the scenes, the AI agent uses the Dart and Flutter MCP Server’s tools to: + +1. **See the error:** It uses a tool to get the current runtime errors from the running application. + +1. **Inspect the UI:** It accesses the Flutter widget tree to understand the layout that is causing the overflow. + +1. **Applies a fix:** Armed with this context, it applies a fix and checks once more for any remaining errors. + +You can then keep or undo the code changes. + +### Adding new functionality with package search + +Imagine you need to add a chart to your app. Which package should you use? How do you add it and write the boilerplate? The Dart and Flutter MCP Server streamlines this entire process. + +<DashImage figure src="images/1DGTEKfS3-ZANFr66VG8MRw.gif" /> + + +**Prompt:** *“Find a suitable package to add a line chart that maps the number of button presses over time.” (Note: For brevity, parts of this recording have been sped up.)* + +The AI agent now acts as a true assistant: + +1. **Find the right tool:** It uses the `pub_dev_search` tool to find popular and highly-rated charting libraries. + +1. **Manage dependencies:** After you confirm its choice (for example, `syncfusion_flutter_charts`), it uses a tool to add the package to your `pubspec.yaml` and runs `pub get`. + +1. **Generate the code:** It generates the new widget code, complete with boilerplate for a line chart that it places in the UI. It even self-corrects syntax errors introduced during the process. You can customize further from there. + +What used to be a multi-step process of research, reading documentation, editing `pubspec.yaml`, and writing the appropriate code in your app, is now a single request. + +## Configuring with popular AI-powered development environments and tools + +Since the Model Context Protocol is rapidly becoming the standard for integrating toolchains and LLMs, there is an ever-growing list of tools that can use MCP servers. The Dart and Flutter MCP Server can be integrated with any MCP client*. Check out the [MCP user guide for a list of example clients](https://modelcontextprotocol.io/clients#feature-support-matrix). + +First, the Dart and Flutter MCP Server requires Dart SDK 3.9/Flutter 3.35 beta or later, so switch to the beta channel and run `flutter upgrade` to ensure you have the latest versions. + +**To access all the features of the Dart and Flutter MCP Server, an MCP client must support [Tools](https://modelcontextprotocol.io/docs/concepts/tools) and [Resources](https://modelcontextprotocol.io/docs/concepts/resources). For the best development experience with the Dart and Flutter MCP Server, an MCP client should also support [Roots](https://modelcontextprotocol.io/docs/concepts/roots).* + +## Gemini CLI + +The [Gemini CLI](https://github.com/google-gemini/gemini-cli) is an open-source AI agent that brings the power of Gemini directly into your terminal, providing a direct path from your prompt to the model. While it excels at coding, the Gemini CLI is a versatile, local utility that you can use for a wide range of tasks, from content generation and problem solving to deep research and task management. + +<DashImage figure src="images/0gD-O6VeS0xT6EdDh.webp" /> + + +Follow the [quickstart instructions](https://github.com/google-gemini/gemini-cli?tab=readme-ov-file#quickstart) to install the Gemini CLI on your machine. + +Ensure your environment is running Dart SDK 3.9/Flutter 3.35 beta or later. Switch channels and run `flutter upgrade` if you need to. + +To configure the Gemini CLI to use the Dart and Flutter MCP Server, edit the `.gemini/settings.json` file in your local project (this only enables the server for this project) or edit the global `~/.gemini/settings.json` file in your home directory (this enables the server for all projects). + +```json +{ + "mcpServers": { + "dart": { + "command": "dart", + "args": [ + "mcp-server", + "--experimental-mcp-server" + ] + } + } +} +``` + + +Pro tips: + +* If you prompt the Gemini CLI with `/mcp`, you will be able to see the set of MCP tools available for use. + +* If your project has specific guidelines or rules that you’d like the LLM to be aware of, you can document those by adding a `GEMINI.md` file to the root of your project. + +## Gemini Code Assist + +*Note: this currently requires the Gemini Code Assist “Insiders” channel. Follow the [Gemini Code Assist instructions](https://developers.google.com/gemini-code-assist/docs/use-agentic-chat-pair-programmer#before-you-begin) to enable the Insiders build.* + +The underlying technology of the Gemini CLI has also been integrated into [Gemini Code Assist](https://codeassist.google/) in VS Code, a powerful AI assistant providing chat and agent capabilities directly in your IDE. In [Agent mode](https://developers.google.com/gemini-code-assist/docs/use-agentic-chat-pair-programmer), you can write a prompt in the chat window and watch as Gemini goes to work to meet your request. Follow the [set up guide](https://developers.google.com/gemini-code-assist/docs/set-up-gemini) to get started with Gemini Code Assist. + +Ensure your environment is running Dart SDK 3.9/Flutter 3.35 beta or later. Switch channels and run `flutter upgrade` if you need to. + +To configure Gemini Code Assist to use the Dart and Flutter MCP Server, use the following instructions: + +1. Install and configure the Gemini CLI first. + +1. Follow the same instructions as those above to configure the Gemini CLI to use the Dart and Flutter MCP Server, which also enables it for Gemini Code Assist. + +You can verify the MCP server has been configured properly by entering `/mcp` in the chat window in Agent mode. + +<DashImage figure src="images/1eYea3SzwoGDCQeP5Tmq-Pg.webp" /> + + +## Firebase Studio + +[Firebase Studio](https://firebase.studio/) is an agentic, cloud-based development environment that helps you build and ship production-quality full-stack AI apps, including APIs, backends, frontends, mobile, and more. + +To add an MCP server in Firebase Studio + +1. Create a `.idx/mcp.json` file in your project (if it doesn’t exist already) and add the same Dart and Flutter MCP Server configuration to the file, as with Gemini CLI instructions above, + +1. Ensure your environment is running Dart SDK 3.9/Flutter 3.35 beta or later. Switch channels and run `flutter upgrade` if you need to. + +1. Rebuild your workspace to complete setup by opening the Command Palette (Shift+Ctrl+P) and entering **Firebase Studio: Rebuild Environment**. + +For more information about MCP server configuration in Firebase Studio, see [Customize your Firebase Studio workspace](https://firebase.google.com/docs/studio/customize-workspace?utm_source=firebase_blog&utm_medium=blog&utm_campaign=FY25-Q3-firebasestudio_cloudsummitlondon&utm_term=firebase_studio_devrel&utm_content=supercharge_firebase_studio_with_mcp#mcp). + +<DashImage figure src="images/0Vjo7hhpe6uDe05yS.webp" /> + + +## GitHub Copilot in VS Code + +[GitHub Copilot](https://code.visualstudio.com/docs/copilot/overview) is an AI-powered coding assistant integrated into VS Code. The Dart VS Code extension provides a simple integration for Copilot by registering the Dart and Flutter MCP Server with VS Code. Copilot, or any other AI agent that supports the VS Code MCP [API](https://code.visualstudio.com/api/extension-guides/mcp), automatically configures the Dart and Flutter MCP Server on your behalf so that you don’t have to do it manually. + +Ensure your environment is running Dart SDK 3.9/Flutter 3.35 beta or later. Switch channels and run `flutter upgrade` if you need to. + +To configure the Dart and Flutter MCP Server, add the following to your VS Code user settings: + +``` +"dart.mcpServer": true +``` + + +You might also consider adding`"chat.mcp.discovery.enabled": true` to your VS Code user settings to automatically discover servers configured in other tools like Claude Desktop or Cursor. + +## Cursor + +[Cursor](https://www.cursor.com/) is a popular AI-powered code editor based on VS Code. The Dart and Flutter VS Code extensions can be installed in Cursor to provide a similar development experience to what you’d expect in VS Code, with additional AI-centric features provided by Cursor. + +Ensure that your environment is running Dart SDK 3.9/Flutter 3.35 beta or later. Switch channels and run `flutter upgrade` if you need to. + +To configure Cursor to use the Dart and Flutter MCP Server, you can either click the **Add to Cursor** button in the Dart and Flutter MCP Server [README](https://github.com/flutter/flutter-intellij/issues/8261) file (easiest method), or follow the instructions to [install MCP servers](https://docs.cursor.com/context/model-context-protocol#installing-mcp-servers) from the official Cursor documentation. After configuring the Dart and Flutter MCP Server, you should end up with the following in your `.cursor/mcp.json` file, which can either be a [project file or a global file](https://docs.cursor.com/context/model-context-protocol#configuration-locations) depending on where you want the MCP server to be enabled: + +```json +{ + "mcpServers": { + "dart": { + "command": "dart", + "args": [ + "mcp-server", + "--experimental-mcp-server", // Can be removed for Dart 3.9.0 or later + "--force-roots-fallback" // Workaround for a Cursor issue with Roots support + ] + } + } +} +``` + + +## Other tools + +The Dart and Flutter MCP Server can be used anywhere that MCP is supported*. Follow the instructions provided by your MCP client of choice to configure the Dart and Flutter MCP Server. + +**Not all tools are supported since some require access to specific IDE services.* + +## What’s coming next? + +This experimental release is just the first step. Our vision is to enable Dart and Flutter developers to reach peak productivity with AI-driven experiences that meet you where you work. Here are some of the key areas we’re investing in next: + +* **Deeper, seamless IDE integration:** We’re working closely with the teams behind Firebase Studio, Gemini in Android Studio, Gemini Code Assist, and others to make using the Dart and Flutter MCP Server effortless. + +* **AI-powered DevTools features:** We plan to bring AI-assistance directly into **Dart and Flutter DevTools**. Instead of manually sifting through complex data, you could simply ask your AI assistant questions like, *“Why did my app stutter here?”* while looking at a timeline trace, *“What’s causing this memory leak?”* when inspecting a memory snapshot, or even *“Help me fix this layout issue”* directly from the Flutter Inspector. + +* **An ever-growing toolbox:** We will continue to expand the MCP server’s capabilities, for example by supporting more complex refactorings. + +While our roadmap is guided by our vision, your [feedback](https://github.com/dart-lang/ai/issues/new/choose) is critical in helping us prioritize what matters most to you. + +## In conclusion + +By giving your coding assistant the context of your project and access to the official toolchain, you can move faster, solve problems more efficiently, and focus on what matters most: building beautiful, high-quality applications. The Dart and Flutter MCP Server represents a significant leap forward in AI-powered development for Dart and Flutter, and is a continuation of our team’s dedication to delivering a productive developer experience. + +This is just the beginning. As the Model Context Protocol becomes more widespread and the Dart and Flutter MCP Server gains more capabilities, the collaboration between you and your AI-powered development tools and agents will only get smarter and more seamless. + +**This feature is still experimental, and your feedback is invaluable.** Try it out today by enabling it in your favorite editor, and let us know what you think by filing an issue on our[ GitHub repository](https://github.com/dart-lang/ai/issues/new/choose). Embrace the future of AI-assisted development and unlock new levels of productivity in your Dart and Flutter projects. We can’t wait to see what you build! \ No newline at end of file diff --git a/sites/www/content/blog/supernova-a-design-system-platform/images/10vWG-4VTlJkHX_9z0nGNWg.webp b/sites/www/content/blog/supernova-a-design-system-platform/images/10vWG-4VTlJkHX_9z0nGNWg.webp new file mode 100644 index 0000000000..1ef1f24e83 Binary files /dev/null and b/sites/www/content/blog/supernova-a-design-system-platform/images/10vWG-4VTlJkHX_9z0nGNWg.webp differ diff --git a/sites/www/content/blog/supernova-a-design-system-platform/images/1GOSImwOIA3oqItUMRmq5Kw.webp b/sites/www/content/blog/supernova-a-design-system-platform/images/1GOSImwOIA3oqItUMRmq5Kw.webp new file mode 100644 index 0000000000..a6a3c44482 Binary files /dev/null and b/sites/www/content/blog/supernova-a-design-system-platform/images/1GOSImwOIA3oqItUMRmq5Kw.webp differ diff --git a/sites/www/content/blog/supernova-a-design-system-platform/images/1J7beI4HVjgoaD4JHk-JUyw.webp b/sites/www/content/blog/supernova-a-design-system-platform/images/1J7beI4HVjgoaD4JHk-JUyw.webp new file mode 100644 index 0000000000..8a6fb4888f Binary files /dev/null and b/sites/www/content/blog/supernova-a-design-system-platform/images/1J7beI4HVjgoaD4JHk-JUyw.webp differ diff --git a/sites/www/content/blog/supernova-a-design-system-platform/images/1YaZp-vdLHW17_b9ktY_4mQ.webp b/sites/www/content/blog/supernova-a-design-system-platform/images/1YaZp-vdLHW17_b9ktY_4mQ.webp new file mode 100644 index 0000000000..4a50ef502b Binary files /dev/null and b/sites/www/content/blog/supernova-a-design-system-platform/images/1YaZp-vdLHW17_b9ktY_4mQ.webp differ diff --git a/sites/www/content/blog/supernova-a-design-system-platform/images/1Zo9xfW_VtnLJmaTYq5ZJyg.webp b/sites/www/content/blog/supernova-a-design-system-platform/images/1Zo9xfW_VtnLJmaTYq5ZJyg.webp new file mode 100644 index 0000000000..d9df3f3fa3 Binary files /dev/null and b/sites/www/content/blog/supernova-a-design-system-platform/images/1Zo9xfW_VtnLJmaTYq5ZJyg.webp differ diff --git a/sites/www/content/blog/supernova-a-design-system-platform/images/1mtTv0oMcbsbStL_T9RSsZw.webp b/sites/www/content/blog/supernova-a-design-system-platform/images/1mtTv0oMcbsbStL_T9RSsZw.webp new file mode 100644 index 0000000000..dce796476c Binary files /dev/null and b/sites/www/content/blog/supernova-a-design-system-platform/images/1mtTv0oMcbsbStL_T9RSsZw.webp differ diff --git a/sites/www/content/blog/supernova-a-design-system-platform/images/1oqPmxo8kmisR_7oiZn06vQ.webp b/sites/www/content/blog/supernova-a-design-system-platform/images/1oqPmxo8kmisR_7oiZn06vQ.webp new file mode 100644 index 0000000000..d81b0db298 Binary files /dev/null and b/sites/www/content/blog/supernova-a-design-system-platform/images/1oqPmxo8kmisR_7oiZn06vQ.webp differ diff --git a/sites/www/content/blog/supernova-a-design-system-platform/index.md b/sites/www/content/blog/supernova-a-design-system-platform/index.md new file mode 100644 index 0000000000..cda202ca65 --- /dev/null +++ b/sites/www/content/blog/supernova-a-design-system-platform/index.md @@ -0,0 +1,100 @@ +--- +title: "Supernova: a design system platform" +description: "Experience of an early adopter building a Flutter web app" +publishDate: 2021-09-30 +author: mariam_hasnany +image: images/1GOSImwOIA3oqItUMRmq5Kw.webp +category: spotlight +layout: blog +--- + +Two years ago, Flutter announced an [early adopter program](https://medium.com/flutter/flutter-for-web-early-adopter-program-now-open-9f1fb146e4c4) for companies that were ready to build with Flutter’s web support. Today one of those early adopters, [Supernova](https://www.supernova.io/), is launching their design system platform, which is both built with Flutter and exports code for Flutter. + +We think Supernova is a good example of the kind of experience Flutter enables on the web. I sat down with Supernova’s CEO [Jiří Třečák](https://twitter.com/JiriTrecak) and CTO [Artem Ufimtcev](https://twitter.com/ArtemUfimtcev) to learn more about what they have built with Flutter’s web support. + +### **How would you describe Supernova to someone who has never heard of it?** + +Usually design and developer teams are compartmentalized and never truly work together; this is because tooling is built with only one or the other discipline in mind. So we decided to build a platform where designers and developers can come together and work as a team. + +<DashImage figure src="images/1GOSImwOIA3oqItUMRmq5Kw.webp" /> + + +### **Tell me more about what sparked the idea for Supernova.** + +The idea stemmed from the need for a certain amount of automation in the design process to make the iterative workflow less cumbersome. What we’re launching today is a tool that automates the process of turning the design system built by designers into something developers can directly consume in the form of code. + +<DashImage figure src="images/1oqPmxo8kmisR_7oiZn06vQ.webp" alt="Supernova, a platform that automates the design process" caption="Supernova, a platform that automates the design process" /> + + +### **Can you go into more detail about the features users can expect in this launch?** + +For this launch you can bring your Figma designs to Supernova, manage style, assets, or other components and then export them into code. We’ve built a documentation editor that lets you create documents based on your design system, and finally, an automation engine that updates your documents and code automatically when design changes are made. + +<DashImage figure src="images/1YaZp-vdLHW17_b9ktY_4mQ.webp" alt="Supernova’s documentation editor" caption="Supernova’s documentation editor" /> + + +*Everything* in our platform is built with Flutter and Dart. There is a lot of sophisticated logic, so our application is more Dart-heavy than most, and Flutter is used for the visualization layer. Alongside Flutter’s CanvasKit renderer, we also have our own Skia engine to parse and render the components from a data model in Figma. + +### **Why did you choose Flutter and Dart to be an integral part of what you’ve built?** +> # Our main consideration for Flutter was that we would be able to bend the platform to our will. + +Originally Supernova was written as a native macOS app, but we saw lots of interest in a web-based solution. Flutter’s promise of deploying to other platforms with one codebase made it an appealing choice. We knew we would eventually want a desktop app as well, as our tool is very compute-heavy and has more demanding performance needs than typical websites. + +Our main consideration for Flutter was that we would be able to bend the platform to our will. We knew we would have a lot of visual operations that are not very common, and instead of building it from scratch using WebGL, Flutter gave us a way to access the lower-layer rendering engine so we can build it in our own way, using a framework that is well tested. + +Despite some early struggles, we feel we made the right decision because of the flexibility that Flutter provided. + +<DashImage figure src="images/10vWG-4VTlJkHX_9z0nGNWg.webp" alt="Supernova is not only built with Flutter but also exports Flutter code" caption="Supernova is not only built with Flutter but also exports Flutter code" /> + + +### **So it wasn’t always smooth sailing. Can you describe some of those struggles and what you learned along the way?** + +One of the very first technical challenges we encountered while working on our Flutter web app was with routing. When the Router API was released, we reworked our routing system to adopt it right away, which proved to be better for web apps. While it offered better integration into browser history, it was far too complicated for nested routing. So instead we made it possible to share state controllers (MobX stores in our case) between pages to simulate nested routing. This approach makes maintenance and implementation much simpler. + +The main thing we learned is the importance of interoperability between Dart and JavaScript specifically for web apps. Using web-specific libraries has limitations if you are also targeting other platforms supported by Flutter, but if your main focus is web apps, it can save a tremendous amount of time. We learned that using JavaScript libraries and embedding HTML can compensate for plugins that lack web support. As an example, we use this approach for a code preview widget, which we embed as HTML and use with a JavaScript library for syntax highlighting. I would say that familiarity with JavaScript helps a great deal when building a Flutter web app, along with understanding the strengths and limitations of Dart/JavaScript interoperability and HTML embedding. + +<DashImage figure src="images/1Zo9xfW_VtnLJmaTYq5ZJyg.webp" alt="Code preview widget built using Dart/JavaScript interoperability" caption="Code preview widget built using Dart/JavaScript interoperability" /> + + +We also learned that tuning performance for web apps with Flutter is not very different to other platforms supported by Flutter. In most cases issues can be resolved in one of two ways: + +1. Localizing widget rebuilds to the smallest possible part of the widget tree. Using MobX as our primary state management system means that rebuilds only affect necessary parts of the UI. + +1. Using lists with on-demand child builders. We try to use `ListView` builders and slivers whenever possible so that we build only what is currently visible on the screen. + +### **Despite the struggles, were there specific technical challenges you experienced that Flutter helped you overcome?** + +Using the CanvasKit renderer through Flutter was a much more pleasant experience than writing shaders from scratch using WebGL. CanvasKit provided us with more higher level tools for rendering, letting us focus on nuances of converting Figma’s non-standard vector models to work with our Skia rendering engine. + +<DashImage figure src="images/1J7beI4HVjgoaD4JHk-JUyw.webp" /> + + +Developers often underestimate the challenges of font management. In our case, we have to support any font the designers might be using. Flutter helps a great deal here, thanks to the [google_fonts package](https://pub.dev/packages/google_fonts) that automatically downloads fonts directly from Google. + +Lastly, Flutter also allows us to share the codebase that powers both the Supernova platform and a separate plugin we built to run in Figma. While there are completely different front ends, they share the same data model and logic. + +### **I’m curious, how did you find using Flutter on the web compared with more traditional frameworks?** + +In general, there are a lot of similarities. Concepts from Flutter, such as its reactive approach to UI, widgets, flexbox layout, and even state management, can be found in many modern web frameworks. + +However, there are a few things that make Flutter different. Unlike other web frameworks, Flutter doesn’t rely on HTML or CSS to write UI components. Instead, you have to learn what widgets exist and how to compose them together to achieve the same UI as what you would’ve otherwise done with CSS. That said, Flutter comes with out-of-box support for Material, so if you’re comfortable with that look and feel, it gets you a long way. +> # I find the philosophy behind Dart quite different. + +The other important difference is the language. While at its core, Dart is similar to JavaScript, I find the philosophy behind Dart quite different. After spending some time writing Dart, you can tell that it’s built for safety and predictability. Language features like sound null safety are uncommon in the world of JavaScript and JavaScript–compatible languages, and we found them crucial for making complex apps stable and maintainable. Another pleasant surprise for us was the annotation processing and code generation capabilities of Dart. + +### **Since you were early adopters, how do you feel Flutter’s web support has improved?** + +We have been using Flutter on the web ever since the first technical preview. Back then, the web target didn’t have support for navigation with browser history, performance was poor, lots of widgets and plugins were missing, and the tooling and debugger were just not there. + +Now with the stable release, we’ve seen progress in all these areas and feel confident in our decision to use Flutter on the web. If you were to compare the technical preview to the Flutter 2.5 release, there are substantial improvements in performance, especially around rendering sophisticated layouts. + +That said, there are still opportunities to improve! In particular, we’re looking forward to seeing better tooling for inspecting CanvasKit rendered widgets, since today’s browser tooling doesn’t support it. + +### **One last question, how can someone get started with Supernova?** + +With today’s launch, Supernova is now available to everyone! To get started, visit [supernova.io](https://www.supernova.io/) to register and provide a link to your Figma design file. Then, basically, the entire world is open to you from there. You can immediately start exporting code for Flutter (or any other framework) or get the assets for icons to start writing documentation. + +<DashImage figure src="images/1mtTv0oMcbsbStL_T9RSsZw.webp" alt="Supernova’s component manager integrated with Figma" caption="Supernova’s component manager integrated with Figma" /> + + +We’re so happy to see one of Flutter’s early adopters launch on the web. Be sure to check it out for yourself! And if you are building with Flutter on the web, please tell us about it. As ever, you can find us on Twitter as [@flutterdev](https://twitter.com/FlutterDev). We can’t wait to see what you’ve created! \ No newline at end of file diff --git a/sites/www/content/blog/supporting-ios-14-and-xcode-12-with-flutter/images/0-1m66Jxnec3s8Qnb.webp b/sites/www/content/blog/supporting-ios-14-and-xcode-12-with-flutter/images/0-1m66Jxnec3s8Qnb.webp new file mode 100644 index 0000000000..e2a37ac271 Binary files /dev/null and b/sites/www/content/blog/supporting-ios-14-and-xcode-12-with-flutter/images/0-1m66Jxnec3s8Qnb.webp differ diff --git a/sites/www/content/blog/supporting-ios-14-and-xcode-12-with-flutter/index.md b/sites/www/content/blog/supporting-ios-14-and-xcode-12-with-flutter/index.md new file mode 100644 index 0000000000..541572d5c0 --- /dev/null +++ b/sites/www/content/blog/supporting-ios-14-and-xcode-12-with-flutter/index.md @@ -0,0 +1,28 @@ +--- +title: "Supporting iOS 14 and Xcode 12 with Flutter" +description: "As always, our goal is to make developers successful on Flutter regardless of the platform you’re targeting. Since the announcement of iOS…" +publishDate: 2020-09-16 +author: csells +category: announcements +layout: blog +--- + +As always, our goal is to make developers successful on Flutter regardless of the platform you’re targeting. Since the announcement of iOS 14 in June, we’ve been working on adding new features to Flutter that support this release, including updates to [support Xcode 12](https://github.com/flutter/flutter/issues/60133), as well as adding new features and improvements to [support iOS 14](https://github.com/flutter/flutter/issues/61047). + +If your iOS 14 app uses [`TextField`](https://api.flutter.dev/flutter/material/TextField-class.html), [`CupertinoTextField`](https://api.flutter.dev/flutter/cupertino/CupertinoTextField-class.html), or [`TextFormField`](https://api.flutter.dev/flutter/material/TextFormField-class.html), you’ll want to make sure that it’s built with Flutter 1.20 or later to ensure that your users are not bothered by unnecessary clipboard notification messages, as per Apple’s updated clipboard policy. + +**Otherwise, existing production apps will run just fine on your end-users’ iOS 14 devices.** However, if you’re upgrading your development phone to iOS 14 today and want to continue to use it with Flutter, you have two choices. + +Your first choice is to use the [latest hotfix release of Flutter](https://github.com/flutter/flutter/wiki/Hotfixes-to-the-Stable-Channel) on the stable channel (1.20.4), which supports development-time deployment to physical iOS 14 devices. Unfortunately, there’s another change that is quite a bit more intrusive (and therefore more risky) which is needed for debugging and hot reload on iOS 14. We’re deferring this fix to our next stable release, which we are planning to release in the next few weeks. + +Your second choice is to use the beta for the upcoming Flutter 1.22 that we’ve released today. This release includes more comprehensive support for iOS 14, including updated visuals, app clips, and Xcode 12 support. We’ll share more about its feature set when it is published to the stable channel, but in the meantime we recommend using this beta if you need support for iOS 14 immediately. + +To obtain the Flutter 1.22 beta, use the following commands: + +``` +$ flutter channel beta +$ flutter upgrade +``` + + +In either case, you’ll want to check out [the Developing for iOS 14 page](https://flutter.dev/docs/development/ios-14) for helpful information about targeting iOS 14 with Flutter. If you have any trouble with iOS 14, please [file issues on the Flutter repo](https://github.com/flutter/flutter/issues). If you have any questions, please post them on [the flutter-dev group](https://groups.google.com/g/flutter-dev). \ No newline at end of file diff --git a/sites/www/content/blog/supporting-six-platforms-with-two-keyboards/images/1RQs5hWCyE30nkyfsVqDb0A.jpeg b/sites/www/content/blog/supporting-six-platforms-with-two-keyboards/images/1RQs5hWCyE30nkyfsVqDb0A.jpeg new file mode 100644 index 0000000000..2ff8207c5f Binary files /dev/null and b/sites/www/content/blog/supporting-six-platforms-with-two-keyboards/images/1RQs5hWCyE30nkyfsVqDb0A.jpeg differ diff --git a/sites/www/content/blog/supporting-six-platforms-with-two-keyboards/index.md b/sites/www/content/blog/supporting-six-platforms-with-two-keyboards/index.md new file mode 100644 index 0000000000..62150e4eff --- /dev/null +++ b/sites/www/content/blog/supporting-six-platforms-with-two-keyboards/index.md @@ -0,0 +1,32 @@ +--- +title: "Supporting six platforms with two keyboards" +description: "Developing Flutter applications for Android, iOS, the web, Windows, macOS, and Linux without losing your sanity" +publishDate: 2022-12-06 +author: domesticmouse +image: images/1RQs5hWCyE30nkyfsVqDb0A.jpeg +category: announcements +layout: blog +--- + +<DashImage figure src="images/1RQs5hWCyE30nkyfsVqDb0A.jpeg" /> + + +The joy of Flutter is the ease of developing applications for six platforms. The pain is verifying your application on six platforms. + +I’m writing a codelab that uses Dart’s Foreign Function Interface (FFI) to integrate C code from iOS, Android, macOS, Linux, and Windows. This story describes how I can develop for all the platforms that Flutter supports without covering my desk in devices. + +Accept that you need more than one machine. As you plan to build your application on at least a subset of the six platforms, consider that this isn’t nearly as bad as it sounds. + +I can develop apps on six platforms with two machines: one macOS laptop and one Windows 11 desktop. Additionally, I have two keyboards, one mouse, and one magic touch pad. + +Here’s why you need the two platforms. + +Start with the obvious: you need a macOS machine to develop for macOS and iOS. Apple says so. Develop for Windows on Windows. It’s what Flutter for Windows supports out of the box, so that’s the paved path. Flutter supports web and Android development on either or both of macOS and Win11. For reasons of keyboard familiarity (I’ve been on macOS for over a decade), I use macOS. + +Windows Subsystem for Linux version 2, or WSL2, on Windows 11 prevented my needing three full keyboards. When you have the Microsoft Windows Subsystem for Linux Product Manager presenting a keynote on WSL2 at Ubuntu Summit to a room full of Linux devotees, you start to understand how much the world has changed over the last twenty years. + +The Linux support in WSL2 extends beyond running graphical Linux applications. Visual Studio Code provides stellar support for WSL2-hosted development. WSL2 runs a headless version of VSCode inside its Linux environment and controls it from a Windows VSCode instance. This keeps Windows and Linux development on one machine with all of the same key shortcuts even if the file system layout differs. + +This said, don’t try to run both your Flutter Windows and Linux development out of the one shared file area. Yes, WSL2 allows you to see the Windows file system from Linux and the Linux file system from Windows. Dart, though, won’t know which binaries to use. Another minor issue would be that the line endings in your source files will get confused as well. Transfer code between Linux and Windows using Git configured with [appropriate line ending mappings](https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings). Your sanity will thank you. + +If you want to develop Flutter on all the platforms Flutter supports, do yourself a favor and grab both a macOS machine and a Windows 11 machine. Your compile times will thank you. \ No newline at end of file diff --git a/sites/www/content/blog/test-flutter-apps-on-travis/images/0-1m66Jxnec3s8Qnb.webp b/sites/www/content/blog/test-flutter-apps-on-travis/images/0-1m66Jxnec3s8Qnb.webp new file mode 100644 index 0000000000..e2a37ac271 Binary files /dev/null and b/sites/www/content/blog/test-flutter-apps-on-travis/images/0-1m66Jxnec3s8Qnb.webp differ diff --git a/sites/www/content/blog/test-flutter-apps-on-travis/index.md b/sites/www/content/blog/test-flutter-apps-on-travis/index.md new file mode 100644 index 0000000000..ae668ca870 --- /dev/null +++ b/sites/www/content/blog/test-flutter-apps-on-travis/index.md @@ -0,0 +1,94 @@ +--- +title: "Test Flutter apps on Travis" +description: "One of my favorite features of Flutter is the widget testing framework. It’s fast and reliable. It feels much more like unit testing rather…" +publishDate: 2017-05-24 +author: yegorj +category: spotlight +layout: blog +--- + +One of my favorite features of Flutter is the widget testing framework. It’s fast and reliable. It feels much more like unit testing rather than user interface testing even though it exercises most of what your UI does: build widgets, perform layout, paint, respond to user actions, animate. + +Animation is a particularly interesting topic. Advanced animation in modern UI is a norm. There are transitions, hero effects, scrolling, sliding, zooming, and much more. Check out [https://material.io](https://material.io/) for a comprehensive list of what comprises a modern user interface. Flutter implements some of the most demanding UI designs (see [https://github.com/flutter/posse_gallery](https://github.com/flutter/posse_gallery), and [Flutter Gallery](https://play.google.com/store/apps/details?id=io.flutter.gallery&hl=en)). One might assume that this would make reliable testing in Flutter nearly impossible. Surprisingly, the reverse is true. Flutter’s [testing framework](https://docs.flutter.io/flutter/flutter_test/flutter_test-library.html) gives you a great level of control over what exactly happens during a frame, animation, transition, or a user action. We run more than 1300 tests on every commit of Flutter, and we rarely see flaky or otherwise unreliable test results. + +To start testing, jump over to [https://flutter.io/testing](https://flutter.io/testing/) and create your first test. + +However, you would also want to run your tests every time a change is made to your application. This is what continuous integration (CI) systems are for. Some CI systems are hosted: Travis, Drone, AppVeyor, GitLab CI. Some you can install in-house: Jenkins, TeamCity, etc. Every time you commit a change, the CI will build your app and run all the tests. If something fails a good CI system will also notify you. + +I’ve used Travis a lot, and I’d like to share how to use it to test Flutter apps. + +## A simple app, a simple test + +If you already know how to write Flutter tests and are only interested in running them on Travis, feel free to skip to the next section. If not, let’s start with a “Hello, World!” app (lib/main.dart): + +```dart +import 'package:flutter/widgets.dart'; + +main() { + runApp(new MyApp()); +} + +class MyApp extends StatelessWidget { + build(_) => new Text('Hello, World!'); +} + +``` + +Now let’s write a test that verifies that “Hello, World!” does indeed appear on the screen (test/app_test.dart): + +```dart +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter_travis_sample/main.dart'; + +void main() { + testWidgets('app should work', (tester) async { + await tester.pumpWidget(new MyApp()); + expect(find.text('Hello, World!'), findsOneWidget); + }); +} + +``` + +If you run `flutter test` you should see something like: + +``` +$ flutter test +00:00 +1: All tests passed! +``` + + +Hurray! Now that we have the test running locally, let’s run it on Travis. + +## Travis + +To get things going on Travis all you need is add the correct `.travis.yml` to the root of your repository and tell Travis to start a continuous build of your repository by following [their instructions](https://docs.travis-ci.com/user/getting-started). Here I’ll only explain what goes in the `.travis.yml` file. Here’s a Linux example: + +```yaml +os: + - linux +sudo: false +addons: + apt: + # Flutter depends on /usr/lib/x86_64-linux-gnu/libstdc++.so.6 version GLIBCXX_3.4.18 + sources: + - ubuntu-toolchain-r-test # if we don't specify this, the libstdc++6 we get is the wrong version + packages: + - libstdc++6 + - fonts-droid-fallback +before_script: + - git clone https://github.com/flutter/flutter.git -b beta + - ./flutter/bin/flutter doctor +script: + - ./flutter/bin/flutter test +cache: + directories: + - $HOME/.pub-cache +``` + +There’s a little bit of boilerplate to setup a Linux environment compatible with Flutter. The important parts are the `before_script` and the `script` sections. The former installs Flutter and runs `flutter doctor` just to make sure everything is setup correctly. The latter runs all the tests, or in our case, the one test we wrote above. + +A complete example can be found [here](https://github.com/yjbanov/flutter_travis_sample). + +You can also build complete APKs and IPAs for your Flutter apps on Travis, but that’s a post for another time. + +Happy coding! \ No newline at end of file diff --git a/sites/www/content/blog/testable-flutter-and-cloud-firestore/images/1pQJzBr81G169df-ffo24Cg.webp b/sites/www/content/blog/testable-flutter-and-cloud-firestore/images/1pQJzBr81G169df-ffo24Cg.webp new file mode 100644 index 0000000000..7d510639ee Binary files /dev/null and b/sites/www/content/blog/testable-flutter-and-cloud-firestore/images/1pQJzBr81G169df-ffo24Cg.webp differ diff --git a/sites/www/content/blog/testable-flutter-and-cloud-firestore/index.md b/sites/www/content/blog/testable-flutter-and-cloud-firestore/index.md new file mode 100644 index 0000000000..e9386c6e4c --- /dev/null +++ b/sites/www/content/blog/testable-flutter-and-cloud-firestore/index.md @@ -0,0 +1,885 @@ +--- +title: "Testable Flutter and Cloud Firestore" +description: "Using dependency injection to test apps using Flutter and Firebase" +publishDate: 2020-10-15 +author: craiglabenz +image: images/1pQJzBr81G169df-ffo24Cg.webp +category: spotlight +layout: blog +--- + +<DashImage figure src="images/1pQJzBr81G169df-ffo24Cg.webp" /> + + +## Why Cloud Firestore? + +The [FlutterFire](https://firebase.flutter.dev/) tech stack, consisting of Flutter and Firebase (and specifically Cloud Firestore), unlock unprecedented development velocity as you build and launch your app. In this article, you’ll explore a robust integration between these two technologies with a focus on testing and using clean architectural patterns. However, instead of jumping straight to the final implementation, you’ll build your way there, one step at a time, so the reasoning behind each step is clear. + +## What you’ll build + +To demonstrate a clean way to implement Cloud Firestore as your app’s backend, you’ll build a modified version of the classic Flutter counter app. The only difference is that the timestamp of each click is stored in Cloud Firestore, and the count displayed is derived from the number of persisted timestamps. You’ll use `Provider` and `ChangeNotifier` to keep the dependencies and state management code clean, and you’ll update the generated test to keep the code *correct*! + +## Before you get started + +This article assumes that you have [watched and followed the steps in this tutorial](https://www.youtube.com/watch?v=Mx24wiPilHg) to integrate your app with Firebase. To recap: + +1. Create a new Flutter project, and call it `firebasecounter.` + +1. Create a Firebase app [in the Firebase console](https://console.firebase.google.com/). + +1. Link your app to iOS and/or Android, depending on your development environment and target audience. +> Note: If you configure your app to work on an Android client, make sure that you [create a `debug.keystore` file](https://gist.github.com/henriquemenezes/70feb8fff20a19a65346e48786bedb8f) before generating your SHA1 certificate. + +After you generate your iOS or Android apps in Firebase, you are ready to proceed. The rest of the video contains great content that you will likely need for real projects, but it’s not required for this tutorial. + +## In case you get stuck + +If any of the steps in this tutorial do not work for you, consult [this public repo](https://github.com/craiglabenz/flutter-firestore-counter), which breaks down the changes into distinct commits. Throughout the tutorial, you will find links to each commit where appropriate. Feel free to use this to verify that you’ve followed along as intended! + +## Create a simple state manager + +To begin the process of integrating your app with Cloud Firestore, you must first refactor the generated code so that the initial `StatefulWidget` communicates with a separate class instead of its own attributes. This allows you to eventually instruct that separate class to use Cloud Firestore. + +Next to your project’s auto-generated main.dart file, create a new file named `counter_manager.dart`, and copy the following code in it: + +``` +class CounterManager { + /// Create a private integer to store the count. Make this private + /// so that Widgets can't modify it directly, but instead must + /// use official methods. + int _count = 0; + + /// Publicly accessible reference to our state. + int get count => _count; + + /// Publicly accessible state mutator. + void increment() => _count++; +} +``` + + +With this code in place, add the following line to the top of `firebasecounter/lib/main.dart`: + +``` +import 'package:firebasecounter/counter_manager.dart'; +``` + + +Then, change `_MyHomePageState`’s code to this: + +``` +class _MyHomePageState extends State<MyHomePage> { + final manager = CounterManager(); + + void _incrementCounter() { + setState(() => manager.increment()); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + Text('You have pushed the button this many times:'), + Text( + '${manager.count}', + style: Theme.of(context).textTheme.headline4, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: Icon(Icons.add), + ), + ); + } +} +``` + + +After saving this code change, your app might appear to crash and show a red error screen. This is because you introduced a new variable, `manager`, whose opportunity to be initialized has passed. This is a common experience with Flutter when you change the way your state *is initialized*, and is easily solved with a hot restart. + +After a hot restart, you should be back where you started: at a count of 0 and able to click the Floating Action Button as much as you want. + +This is a good time to run the single test that Flutter provides in any new project. You can find its definition at `test/widget_test.dart`, and execute it by running: + +``` +$ flutter test +``` + + +Assuming that the test passes, you should be ready to continue! +> Note: If you got stuck in this section, compare your changes to [this commit](https://github.com/craiglabenz/flutter-firestore-counter/commit/483dd3b3833bf710b04db4a3ba347b1d1ecbe5de) in the tutorial’s repo. + +## Persist timestamps + +The initial app description mentioned persisting the timestamp of each click. So far, you haven’t added any infrastructure to satisfy that second requirement, so create another new file named `app_state.dart`, and add the following class: + +``` +/// Container for the entirety of the app's state. An instance of +/// this class should be able to inform what is rendered at any +/// point in time. +class AppState { + /// Full click history. For super important auditing purposes. + /// The count of clicks becomes this list's `length` attribute. + final List<DateTime> clicks; + + /// Default generative constructor. Const-friendly, for optimal + /// performance. + const AppState([List<DateTime> clicks]) + : clicks = clicks ?? const <DateTime>[]; + + /// Convenience helper. + int get count => clicks.length; + + /// Copy method that returns a new instance of AppState instead + /// of mutating the existing copy. + AppState copyWith(DateTime latestClick) => AppState([ + latestClick, + ...clicks, + ]); +} +``` + + +From this point forward, the `AppState` class’s job is to represent the state of what should be rendered. The class contains no method that can mutate itself, only a single `copyWith` method that other classes will use. + +Keeping testing in mind, you can begin making changes to the `CounterManager` concept. Having a single class won’t work in the long run, because the app eventually interacts with Cloud Firestore. Yet you don’t want to create real records every time you run the tests. To that end, you need an abstract interface that defines how the app should behave. + +Open `counter_manager.dart` again, and add the following code at the top of the file: + +``` +import 'package:firebasecounter/app_state.dart'; + +/// Interface that defines the functions required to manipulate +/// the app state. +/// +/// Defined as an abstract class so that tests can operate on a +/// version that does not communicate with Firebase. +abstract class ICounterManager { + /// Any `CounterManager` must have an instance of the state + /// object. + AppState state; + + /// Handler for when a new click must be stored. Does not require + /// any parameters, because it only causes the timestamp to + /// persist. + void increment(); +} +``` + + +The next step is to update `CounterManager` to explicitly descend from `ICounterManager`. Update its definition to this: + +``` +class CounterManager implements ICounterManager { + AppState state = AppState(); + + void increment() => state = state.copyWith(DateTime.now()); +} +``` + + +At this point, our helper code looks pretty good, but `main.dart` has fallen behind. There is no reference to `ICounterManager` in `main.dart`, when, in fact, that is the *only* Manager class it should know about. In `main.dart`, update apply the following changes: + +1. Add the missing import to the top of the `main.dart`: + +``` +import 'package:firebasecounter/app_state.dart'; +``` + + +2. Update `_MyHomePageState` as follows: + +``` +class _MyHomePageState extends State<MyHomePage> { + final ICounterManager manager; + _MyHomePageState({@required this.manager}); + + void _incrementCounter() => setState(() => manager.increment()); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + Text('You have pushed the button this many times:'), + Text( + '${manager.state.count}', + style: Theme.of(context).textTheme.headline4, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: Icon(Icons.add), + ), + ); + } +} +``` + + +This change should remove any red squiggly lines in your IDE from `_MyHomePageState`, but now `MyHomePage` complains because its `createState()` method doesn’t supply all required arguments to `_MyHomePageState`. You could make `MyHomePage` require this variable and pass the object through to its `State`-based class, but that could lead to long chains of widgets requiring and passing objects that they don’t actually care about, simply because some descendent widget requires it and some ancestor widget supplies it. Clearly, this needs a better strategy. + +Enter: [Provider](https://pub.dev/packages/provider) + +## Using `Provider` to access application state + +`Provider` is a library that streamlines the use of Flutter’s `InheritedWidget` pattern. `Provider` allows a widget high in your widget tree to be directly accessible by all of its descendants. This may feel like a global variable, but the alternative is to pass your data models down through every intermediate widget, many of whom will have no intrinsic interest in them. This “variables [bucket brigade](https://en.wikipedia.org/wiki/Bucket_brigade)” anti-pattern blurs your app’s separation of concerns and can make refactoring layouts unnecessarily tedious. `InheritedWidget` and `Provider` bypass those problems by allowing widgets anywhere in your widget tree to get the data models they need directly. + +To add `Provider` to your application, open `pubspec.yaml`, and add it under the dependencies section: + +``` +dependencies: + flutter: + sdk: flutter + # Add this + provider: ^4.3.2+2 +``` + + +After adding that line to your `pubspec.yaml` file, run the following to download `Provider` onto your machine: + +``` +$ flutter pub get +``` + + +Next to `main.dart`, create a new file named `dependencies.dart` and copy the following code into it: + +``` +import 'package:firebasecounter/counter_manager.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class DependenciesProvider extends StatelessWidget { + final Widget child; + DependenciesProvider({@required this.child}); + + @override + Widget build(BuildContext context) { + return MultiProvider( + providers: [ + Provider<ICounterManager>(create: (context) => CounterManager()), + ], + child: child, + ); + } +} +``` + + +A few notes about `DependenciesProvider`: + +1. It uses `MultiProvider`, despite having only one entry in its list. This technically could be collapsed to a single `Provider` widget, but a real app will likely contain many such services, so it’s often best to start with `MultiProvider` right away. + +1. It requires a `child` widget, which follows the Flutter convention for widget composition and allows us to insert this helper near the top of the widget tree, making the `ICounterManager` instance available to the entire app. + +Next, make the new `DependenciesProvider` available to the entire app. A simple way to do this is to wrap the entire `MaterialApp` widget with it. Open `main.dart`, and update the `main` method to look like this: + +``` +void main() { + runApp( + DependenciesProvider(child: MyApp()), + ); +} +``` + + +You also need to import `dependencies.dart` in `main.dart`: + +``` +import 'package:firebasecounter/dependencies.dart'; +``` + + +## Using a `Consumer` widget + +You already saw the `MultiProvider` widget in action (which is really just a nicer way to declare a series of single `Provider` widgets). The next step is to access the `ICounterManager` object by using the [`Consumer`](https://pub.dev/documentation/provider/latest/provider/Consumer-class.html) widget. + +## Dependency injection + +If you’ve written a Flutter application using Cloud Firestore, then you probably discovered that Firestore can make good unit tests harder to write. After all, how do you avoid generating real records in your database when a Firestore integration is wired directly into your widget tree? + +If you’ve had this experience, then you found the limitations of baking your dependencies directly into your UI code, which, in Flutter’s case, is widgets. This is the power of dependency injection: if your widgets accept helper classes that facilitate their interaction with dependencies (like Firebase, the device’s file system, or even network requests), then you can supply mocks or fakes instead of the real classes during tests. This allows you to test whether your widgets behave as expected without waiting on slow network requests, filling up your filesystem, or incurring Firebase billing charges. + +To achieve this, you need to refactor the app so that there is a clean point where the tests can inject fakes that mimic real Cloud Firestore behavior. Luckily, the `Consumer` widget is perfect for this job. + +Open `main.dart` and replace your `MyApp` widget with the following code: + +``` +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: Consumer<ICounterManager>( + builder: (context, manager, _child) => MyHomePage( + manager: manager, + title: 'Flutter Demo Home Page', + ), + ), + ); + } +} +``` + + +Also, import `Provider` at the top of `main.dart`: + +``` +import 'package:provider/provider.dart'; +``` + + +Wrapping `MyHomePage` in a `Consumer` widget allows you to reach arbitrarily high in the widget tree to access the desired resources and inject them into the widgets that need them. It may feel like unnecessary work in this tutorial, because you only reach back one layer to `MyApp()`, but this could stretch through dozens of widgets in real production apps. + +Next, in the same file, make this edit to `MyHomePage`: +> Note: Don’t worry if you see a red screen after saving this change. More edits are needed to complete the refactor! + +``` +class MyHomePage extends StatefulWidget { + final ICounterManager manager; + MyHomePage({@required this.manager, Key key, this.title}) : super(key: key); + + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} +``` + + +This simple constructor change allows the code to accept the variable passed in the previous snippet. + +Finally, complete the refactor by making this edit to `_MyHomePageState`: + +``` +class _MyHomePageState extends State<MyHomePage> { + + // No longer expect to receive a `ICounterManager object` + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + Text('You have pushed the button this many times:'), + Text( + // Reference `widget.manager` instead of + // `manager` directly + '${widget.manager.state.count}', + style: Theme.of(context).textTheme.headline4, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + // Reference `widget.manager` instead of `manager` directly + onPressed: () => setState(() => widget.manager.increment()), + tooltip: 'Increment', + child: Icon(Icons.add), + ), + ); + } +} +``` + +> Note: You will likely have to perform a hot restart to fix your app. + +As you might recall, all `State` objects contain a reference to their containing `StatefulWidget` wrappers in the `widget` attribute. Thus, the `_MyHomePageState` object can access this new `manager` attribute by changing its code from `manager` to `widget.manager`. + +And, that’s it! You’ve injected dependencies into the widgets that need them instead of hardcoding production implementations. + +## Test the app + +If you run `flutter test` right now, you’ll see that the test suite no longer passes. When you inspect `widget_test.dart`, the reason might be clear: the test function instantiates `MyApp()`, but doesn’t wrap it with `DependenciesProvider` like you did in the real code, so the `Consumer` widget added within `MyApp` cannot find a satisfying `Provider` in its ancestor widgets. + +This is where dependency injection begins to pay dividends. Instead of mimicking the production code in tests (by wrapping `MyApp` with `DependenciesProvider`), change the test to initialize `MyHomePage`. Update `widget_test.dart` to look like this: + +``` +import 'package:firebasecounter/counter_manager.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:firebasecounter/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget( + MaterialApp( + home: MyHomePage( + manager: CounterManager(), + title: 'Test Widget', + ), + ), + ); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} +``` + + +By using a `MyHomePage` instance directly (along with a wrapping `MaterialApp` to provide valid `BuildContext` objects), you have set yourself up to have a unit-tested integration to Cloud Firestore! +> Note: If you got stuck in this section, compare your changes to [this commit](https://github.com/craiglabenz/flutter-firestore-counter/commit/bb68c1d3bb3746eca5f2dea16bd799c98ff232f1) in the tutorial’s repo. + +## Implementing Cloud Firestore + +So far, you’ve moved around a lot of code and introduced several helper classes, but you haven’t changed anything about how the app works. The good news is that everything is in place to begin writing some code that knows about Cloud Firestore. To start, open `pubspec.yaml`, and add these two lines: + +``` +dependencies: + # Add this + cloud_firestore: ^0.14.1 + # Add this + firebase_core: ^0.5.0 + flutter: + sdk: flutter + provider: ^4.3.2+2 +``` + + +As always when you apply changes to `pubspec.yaml` (unless your IDE does this for you), run the following command to download and link your new libraries: + +``` +$ flutter pub get +``` + +> Note: If you have not yet created your database: visit the Firebase console for your project, click on the **Cloud Firestore** tab, and click the **Create Database** button. + +## Waiting on Firebase + +The first step to successfully use Cloud Firestore is to initialize Firebase and, most critically, *not attempting to use any Firebase resources until this task is successful*. Luckily, you can contain that logic with one `StatefulWidget` instead of sprinkling that task all over your code. + +Create a new file at `firebasecounter/lib/firebase_waiter.dart` and add the following code: + +``` +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/material.dart'; + +class FirebaseWaiter extends StatefulWidget { + final Widget Function(BuildContext) builder; + final Widget waitingChild; + const FirebaseWaiter({ + @required this.builder, + this.waitingChild, + Key key, + }) : super(key: key); + + @override + _FirebaseWaiterState createState() => _FirebaseWaiterState(); +} + +class _FirebaseWaiterState extends State<FirebaseWaiter> { + Future<FirebaseApp> firebaseReady; + + @override + void initState() { + super.initState(); + firebaseReady = Firebase.initializeApp(); + } + + @override + Widget build(BuildContext context) => FutureBuilder<FirebaseApp>( + future: firebaseReady, + builder: (context, snapshot) => // + snapshot.connectionState == ConnectionState.done + ? widget.builder(context) + : widget.waitingChild, + ); +} +``` + + +This class uses the pattern in Flutter of leveraging certain widgets to completely handle a specific dependency or problem within your app. To use this `FirebaseWaiter` widget, return to `main.dart`, and apply the following change to `MyApp`: + +``` +// Add this import at the top +import 'package:firebasecounter/firebase_waiter.dart'; + +// Replace `MyApp` with this +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: FirebaseWaiter( + builder: (context) => Consumer<ICounterManager>( + builder: (context, manager, _child) => MyHomePage( + manager: manager, + title: 'Flutter Demo Home Page', + ), + ), + // This is a great place to put your splash page! + waitingChild: Scaffold( + body: const Center(child: CircularProgressIndicator()), + ), + ), + ); + } +} +``` + + +Now, the app is able to wait for Firebase’s initialization, but can skip this process during tests by simply not using `FirebaseWaiter`. +> Note: The above changes may cause Flutter to complain about missing Firebase plugins. If it does, completely kill your app and start debugging again, which allows Flutter to install all platform-specific dependencies. + +## Getting data from Cloud Firestore + +First, import Cloud Firestore by adding the following line to the top of `counter_manager.dart`: + +``` +import 'package:cloud_firestore/cloud_firestore.dart'; +``` + + +Next, also in `counter_manager.dart`, add the following class: + +``` +class FirestoreCounterManager implements ICounterManager { + AppState state; + final FirebaseFirestore _firestore; + +FirestoreCounterManager() + : _firestore = FirebaseFirestore.instance, + state = const AppState() { + _watchCollection(); + } + +void _watchCollection() { + // Part 1 + _firestore + .collection('clicks') + .snapshots() + // Part 2 + .listen((QuerySnapshot snapshot) { + // Part 3 + if (snapshot.docs.isEmpty) return; + // Part 4 + final _clicks = snapshot.docs + .map<DateTime>((doc) { + final timestamp = doc.data()['timestamp']; + return (timestamp != null) + ? (timestamp as Timestamp).toDate() + : null; + }) + // Part 5 + .where((val) => val != null) + // Part 6 + .toList(); + // Part 7 + state = AppState(_clicks); + }); + } + + @override + void increment() { + _firestore.collection('clicks').add({ + 'timestamp': FieldValue.serverTimestamp(), + }); + } +} +``` + +> Note: This class is almost correct, but creates a bug that is explored later. If you add this code to your app and run it right now, you will see that the behavior is not what you want. Read on for a thorough explanation of what is happening! + +There’s a lot going on here, so let’s step through it. + +First, `FirestoreCounterManager` implements the `ICounterManager` interface, so it’s an eligible candidate to use in production widgets. (Eventually, it will be supplied by`DependenciesProvider`!) `FirestoreCounterManager` also maintains an instance of `FirebaseFirestore`, which is the live connection to the production database. `FirestoreCounterManager` also calls `_watchCollection()` during its initialization to set up a connection to the specific data you care about, and this is where things get interesting. + +The `_watchCollection()` method does a lot and deserves its own examination. + +In Part 1, `_watchCollection()` calls `_firestore.collection('clicks').snapshots()`, which returns a stream of updates any time data in the collection changes. + +In Part 2, `_watchCollection()` immediately registers a listener to that stream using `.listen()`. The callback passed to `listen()` receives a new `QuerySnapshot` object on each change to the data. This update object is called a snapshot because it reflects the correct state of the database at one time, but, at any point, could be replaced by a new snapshot. + +In Part 3, the callback short-circuits if the collection is empty. + +In Part 4, the callback loops over the snapshot’s documents and returns a list of mixed `null` and `DateTime` values. + +In Part 5, the callback discards any `null` values. These arise from the bug that will be fixed shortly, but this sort of defensive coding is always a good idea when dealing with data from Cloud Firestore. + +In Part 6, the callback addresses the fact that `map()` returns an iterator, not a list. Calling `.toList()` on an iterator forces it to process the entire collection, which is what you want. + +And last, in Part 7, the callback updates the `state` object. + +To use the new class, open `dependencies.dart`, and replace its contents with this: + +``` +import 'package:firebasecounter/counter_manager.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class DependenciesProvider extends StatelessWidget { + final Widget child; + DependenciesProvider({@required this.child}); + +@override + Widget build(BuildContext context) { + return MultiProvider( + providers: [ + Provider<ICounterManager>( + create: (context) => FirestoreCounterManager()), + ], + child: child, + ); + } +} +``` + + +## Diagnosing the bug + +If you run this code as is, you’ll *almost* see the desired behavior. Everything seems correct, except the screen is always rendered one click behind reality. What is happening? + +The issue arises from an incompatibility with the initial counter implementation and the current, stream-based implementation. The `FloatingActionButton`’s `onPressed` handler looks like this: + +``` +floatingActionButton: FloatingActionButton( + onPressed: () => setState(() => widget.manager.increment()), + ... +) +``` + + +That handler calls `increment()` and immediately invokes `setState()`, which tells Flutter to re-render. + +This worked great when synchronously updating state held in the device’s memory. However, the new stream-based implementation starts a series of asynchronous steps. This means that, as-is, the code calls `setState()` immediately and then, only at an unknown future point, does the `manager` object update its `state` attribute. In short, the `setState()` call in the `onPressed` handler is happening too early! What’s worse, because all this activity happens inside a callback, deep within `FirestoreCounterManager` that no widgets know anything about, there is no `Future` that the widgets can `await` to solve the problem. + +It’s almost as if the `manager` object needs to be able to tell the widgets when to redraw. 🤔 + +Enter: [ChangeNotifier](https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple#changenotifier) +> Note: If you got stuck during this section, compare your changes with [this commit](https://github.com/craiglabenz/flutter-firestore-counter/commit/3bf17b9bfac6c907b8650e1c668fa19b1160a51d) in the public repo. These changes include Xcode and build.gradle changes resulting from adding Firebase, but you can likely focus on changes to the Dart files. + +## Using `ChangeNotifier` to re-render the widget tree + +`ChangeNotifier` is a class that does exactly what its name suggests: it notifies widgets when changes occur that require a re-render. + +The first step in this process is to update the `ICounterManager` interface to extend `ChangeNotifier`. To do this, open `firebasecounter/lib/counter_manager.dart`, and make the following changes to the `ICounterManager` declaration: + +``` +// Add `extends ChangeNotifier` to your declaration +abstract class ICounterManager extends ChangeNotifier { + // Everything inside the class is the same. +} +``` + + +If you haven’t imported `flutter/material.dart` yet, open `firebasecounter/lib/counter_manager.dart`, and add it to the top: + +``` +import 'package:flutter/material.dart'; +``` + + +You’re now ready to update the definitions of `CounterManager` and `FirestoreCounterManager`. For `CounterManager`, replace its code with the following implementation: + +``` +class CounterManager extends ChangeNotifier implements ICounterManager { + AppState state = AppState(); + + /// Copies the state object with the timestamp of the most + /// recent click and tells the stream to update. + void increment() { + state = state.copyWith(DateTime.now()); + // Adding this line is how `ChangeNotifier` tells widgets to + // re-render themselves. + notifyListeners(); + } +} +``` + + +And, for `FirebaseCounterManager`, apply the following changes: + +1. Edit its signature to match this: + +``` +class FirestoreCounterManager extends ChangeNotifier + implements ICounterManager { + ... +} +``` + + +2. Add the same `notifyListeners();` line to the end of `_watchCollection()`, as follows: + +``` +void _watchCollection() { + _firestore + .collection('clicks') + .snapshots() + .listen((QuerySnapshot snapshot) { + + // Generation of `_clicks` omitted for clarity, but do not + // change that code. + + state = AppState(_clicks); + + // The only change necessary is to add this line! + notifyListeners(); + }); +} +``` + + +You’ve now set up half of the changes necessary for the `ICounterManager` classes to tell the widgets to re-render any time the data changes. The `Manager` classes are telling the widgets to re-render, but if you run your app now, you’ll see that the widgets aren’t listening. + +To fix this, open `dependencies.dart` and replace the implementation of `DependenciesProvider` with the following: + +``` +class DependenciesProvider extends StatelessWidget { + final Widget child; + DependenciesProvider({@required this.child}); + + @override + Widget build(BuildContext context) { + return MultiProvider( + providers: [ + // `Provider` has been replaced by ChangeNotifierProvider + ChangeNotifierProvider<ICounterManager>( + create: (context) => FirestoreCounterManager(), + ), + ], + child: child, + ); + } +} +``` + + +As a last change, remove `setState` from `_MyHomePageState` to skip an unnecessary re-render. Update its `FloatingActionButton` to look like this: + +``` + floatingActionButton: FloatingActionButton( + // Remove setState()! + onPressed: widget.manager.increment, + tooltip: 'Increment', + child: Icon(Icons.add), + ), +``` + + +And, that’s it! `ChangeNotifierProvider` makes sure that the widgets are “listeners”, so that when `notifyListeners()` is called by an `ICounterManager` class, the widgets get the message to re-render. + +At this point, you should be able to hot restart your app and see everything working! + +Note: If you got stuck during this section, compare your changes against [this commit](https://github.com/craiglabenz/flutter-firestore-counter/commit/dfb584f62d094d8fdb6067ea11ff3551b9186aed) of the public repo. + +## Fixing the tests + +While the last round of changes successfully implemented the desired functionality, unfortunately, they also broke the tests. Next, you’ll apply a few more tweaks to get everything working again, and you’ll be done. + +In `widget_test.dart`, the code passes a `CounterManager` instance directly without an accompanying `ChangeNotifierListener`. The way this was handled in the widget tree was to wrap everything in `DependenciesProvider`, but that class knows about Firestore, and the whole point of this is to keep Firestore out of the tests. + +One solution is to create `TestDependenciesProvider`, which can contain the testing versions of all of the dependencies. Open `firebasecounter/lib/dependencies.dart`, and add the following class: + +``` +class TestDependenciesProvider extends StatelessWidget { + final Widget child; + TestDependenciesProvider({@required this.child}); + + @override + Widget build(BuildContext context) { + return MultiProvider( + providers: [ + ChangeNotifierProvider<ICounterManager>( + create: (context) => CounterManager(), + ), + ], + child: child, + ); + } +} +``` + + +This class is almost identical to `DependenciesProvider`, but `TestDependenciesProvider` provides an instance of `CounterManager()` instead of `FirestoreCounterManager()`. + +Now, in `test/widget_test.dart`, update the test widget initialization to this: + +``` +await tester.pumpWidget( + TestDependenciesProvider( + child: MaterialApp( + home: Consumer<ICounterManager>( + builder: (context, manager, _child) => MyHomePage( + manager: manager, + title: 'Flutter Test Home Page', + ), + ), + ), + ), +); +``` + + +If you haven’t yet, add these two imports near the top of `test/widget_test.dart`: + +``` +import 'package:firebasecounter/dependencies.dart'; +import 'package:provider/provider.dart'; +``` + + +Run your tests again, and, viola! + +Note: If you got stuck during this section, compare your changes against [this commit](https://github.com/craiglabenz/flutter-firestore-counter/commit/cb8c876abfa80b013bb122ed289163ab5587f5cc) of the public repo. + +## Wrapping up + +In this article, you remodeled the classic Flutter counter app so that it persists all activity to Cloud Firestore. You also avoided mixing business logic into any widgets, meaning that the app is easy to test. + +The state management techniques covered here are viable for many apps, but they aren’t the only or best ways. The Flutter community is rich with excellent state management solutions that are worth your investigation. Here are a few to consider: + +1. [Flutter Bloc](https://pub.dev/packages/flutter_bloc) is particularly useful for anyone with [Redux](https://redux.js.org/) experience. + +1. This [Flutter Firebase and DDD video tutorial series](https://www.youtube.com/playlist?list=PLB6lc7nQ1n4iS5p-IezFFgqP6YvAJy84U) by [Reso Coder](https://www.youtube.com/resocoder) walks you through the entire process of using Flutter Bloc, Cloud Firestore, and several other excellent libraries. + +1. This [more direct port of Redux](https://pub.dev/packages/flutter_redux) is also popular. + +1. The newest entry on the list; the creator of `Provider` released a new package, [Riverpod](https://pub.dev/packages/riverpod), which is `Provider`’s successor. + +For more information on state management, see the [State Management docs](https://flutter.dev/docs/development/data-and-backend/state-mgmt/intro) on flutter.dev. + +Whichever state management solution you choose, good luck, and happy coding! \ No newline at end of file diff --git a/sites/www/content/blog/that-flutters-reload-is-so-hot-right-now/images/0BUpIm2yR_KoNcT82.jpg b/sites/www/content/blog/that-flutters-reload-is-so-hot-right-now/images/0BUpIm2yR_KoNcT82.jpg new file mode 100644 index 0000000000..436596f48e Binary files /dev/null and b/sites/www/content/blog/that-flutters-reload-is-so-hot-right-now/images/0BUpIm2yR_KoNcT82.jpg differ diff --git a/sites/www/content/blog/that-flutters-reload-is-so-hot-right-now/images/17BKoEbJbiH08LxyMIpQdew.webp b/sites/www/content/blog/that-flutters-reload-is-so-hot-right-now/images/17BKoEbJbiH08LxyMIpQdew.webp new file mode 100644 index 0000000000..799d83284a Binary files /dev/null and b/sites/www/content/blog/that-flutters-reload-is-so-hot-right-now/images/17BKoEbJbiH08LxyMIpQdew.webp differ diff --git a/sites/www/content/blog/that-flutters-reload-is-so-hot-right-now/index.md b/sites/www/content/blog/that-flutters-reload-is-so-hot-right-now/index.md new file mode 100644 index 0000000000..71089d041a --- /dev/null +++ b/sites/www/content/blog/that-flutters-reload-is-so-hot-right-now/index.md @@ -0,0 +1,199 @@ +--- +title: "Flutter’s hot reload is so hot right now." +description: "Testing how Flutter’s hot reload scales up." +publishDate: 2018-03-13 +author: swavkulinski +image: images/17BKoEbJbiH08LxyMIpQdew.webp +category: tutorial +layout: blog +--- + +<DashImage figure src="images/17BKoEbJbiH08LxyMIpQdew.webp" /> + + +When it comes to discussing hot reload there is really no chance to avoid a Zoolander meme so please forgive me the title. + +One of the most popular features of Flutter framework is its **hot reload**. It allows reloading the code on the running app without losing the state, massively reducing development time. What is really important is that Flutter’s hot reload isn’t a feature which was added to the framework, like **Instant Run** in AndroidStudio. Flutter was designed this way from day one and it just leverages the power of its architecture. Flutter is fairly young but mobile companies already try to tackle full-scale development. I have noticed this by observing the shift in questions asked on [https://gitter.im/flutter/flutter](https://gitter.im/flutter/flutter) and on meetups. The initial wave of questions : +> How does it draw? +Why does it use Dart? +How it differs from other frameworks? + +start to fade away and give more more time questions rooted in large-scale development. +> How does it scale? +What does CI support look like? +How automated testing looks like? + +During last Flutter meetup [@Skillsmatter](http://twitter.com/Skillsmatter) one of the developers asked a quite serious question: +> *Will hot reload slow down as the project gets bigger? How much?* + +My own projects in Flutter have never got bigger than a couple of dozens of classes and a handful of screens. My hot reload always stays steady at 650 ms, hence my curiosity. + +## So where is the breaking point? + +This is why I have decided to create a dummy project to take hot reload for a spin and push the envelope. I’ve built a simple code generator written in — no surprise here — **Dart**. Dart has a bunch of tricks under its belt which come in very handy when generating code. Dart is completely insensitive to **trailing commas**. This simplifies the code generation algorithm massively. You no longer need to branch your logic when processing the last element in the list. + +## The Sample + +My first attempt was quite simplistic. + +1) The generator will create a specific number of stateless widgets +2) The generator will create a layout which nests them. +3) I will manually modify generated .dart file and observe hot reload time + +Below is an example of one of the generated stateless widget class. + +```dart +class GeneratedWidget0 extends StatelessWidget { +  + final Widget child; + + GeneratedWidget0({this.child}); + + Widget build(BuildContext context) { +  return new Center( +  child: new DecoratedBox( +  decoration: new BoxDecoration(border: new Border( +  top: const BorderSide(color: Colors.blue,width: 0.5,style: BorderStyle.solid), +  bottom: const BorderSide(color: Colors.blue,width: 0.5,style: BorderStyle.solid), +  left: const BorderSide(color: Colors.blue,width: 0.5,style: BorderStyle.solid), +  right: const BorderSide(color: Colors.blue,width: 0.5,style: BorderStyle.solid), +  )), +  child: new Padding(  +  padding: new EdgeInsets.all(1.1), +  child: child, +  ) +  )  +  ); + } +} +``` + +The generated layout looks like this: + +```dart +import ‘package:flutter/material.dart’; +  + class MainLayout extends StatelessWidget { +  +@override + Widget build(BuildContext context) { +  return new Material( child: new GeneratedWidget4 ( + child: new GeneratedWidget3 ( + child: new GeneratedWidget2 ( + child: new GeneratedWidget1 ( + child: new GeneratedWidget0 ( + child: new GeneratedWidget0 ( + child: new Text(‘test with 5 items’,style: Theme.of(context).textTheme.body1,), + ), + ), + ), + ), + ), + ); +} + +``` + +## The Test + +I’ve started gently with 20 widgets. As I was expecting no reload could jump over 600ms. So I’ve pushed the throttle a bit with 80 widgets. Still no change. +I have decided to go nuts and hit it with 1000 nested widgets. + +The result was interesting. Compiler hit me with an error: + +``` +error: line 265 pos 8: stack overflow while parsing +``` + + +By trial and error, I’ve concluded that it takes roughly about 250 unique nested widgets to get this error. This wasn’t a result I was looking for. I wanted to see how hot reload slows down, not where compiler tells me that I am insane. + +I had to find the way to pass the limit nested widgets without breaking the parser. + +I modified the generator to create a number of packages, each one with a generated file like the one above, but all with distinct names. Then I added a generator for a **router** which registered each package as the navigator’s target. This way I was sure I’d used all my classes. + +Below you can see the router for 5 packages. + +```dart + +import ‘package:flutter/material.dart’; +import ‘package_0/generated_widget.dart’; +import ‘package_1/generated_widget.dart’; +import ‘package_2/generated_widget.dart’; +import ‘package_3/generated_widget.dart’; +import ‘package_4/generated_widget.dart’; + +class Router extends StatelessWidget { + + @override build(BuildContext context) { + + return new MaterialApp( +  title: ‘Flutter Demo’, +  theme: new ThemeData( +  primarySwatch: Colors.blue, +  ), +  routes : { +  ‘/’ : (context) => new Package0MainLayout(onTapHandler:()=>Navigator.of(context).pushNamed(‘/route_1’)), + ‘/route_1’ : (context) => new Package1MainLayout(onTapHandler:()=>Navigator.of(context).pushNamed(‘/route_2’)), + ‘/route_2’ : (context) => new Package2MainLayout(onTapHandler:()=>Navigator.of(context).pushNamed(‘/route_3’)), + ‘/route_3’ : (context) => new Package3MainLayout(onTapHandler:()=>Navigator.of(context).pushNamed(‘/route_4’)), + ‘/route_4’ : (context) => new Package4MainLayout(onTapHandler:()=>Navigator.of(context).pushNamed(‘/’)), + }, +  ); +  } +} + +``` + +Now I was able to pack more classes into more layouts without hitting the “magic 250 nested widgets wall”. I was out of the woods with the parser problem and able to continue my tests. + +**80 widgets x 1 package — *one class modified*** — 650 ms. +**80 widgets x 5 packages — *5 classes modified*** — 650 ms. + +However this one returned with a warning + +``` +Some program elements were changed during reload but did not run when the view was reassembled; +you may need to restart the app (by pressing “R”) for the changes to have an effect. + • Package4MainLayout.build (lib/package_4/generated_widget.dart:10) + • Package3MainLayout.build (lib/package_3/generated_widget.dart:10) + • Package2MainLayout.build (lib/package_2/generated_widget.dart:10) + +``` + + +then I added 75 more packages and the result was a Dart VM timeout error: + +``` +TimeoutException: Request to Dart VM Service timed out +… +Application finished. + +``` + + +I restarted Flutter and continued from 80 x 80. Then I pressed reload and noticed + +``` +Reloaded 82 of 644 libraries in 1,209ms. +``` + + +OK. So **it depends on how many classes we modify**, not on the size of the project itself. Flutter reloaded 80 widgets in 80 packages in slightly more than 1.2 seconds. I’ve realised that the reason why I was receiving same 650 ms result was one of the Dart’s and Flutter’s strongest side — **immutability**. Dart and Flutter reuse most of the old object when creating new state. Only parts which are inevitably modified are replaced. + +Smart, this means if we stick to one place in the code during development we will get 650 ms every time. My thoughts went straight to Uncle Bob’s lessons. This tool is perfect for TDD. + +It was time for a pedal to the metal. **80 widgets x 250 packages** — if you are developing an application which has 250 unique layouts /screens— please stop it now. Be considerate of user’s time, and your own! + +The result of this craziness was: + +``` +Reloaded 252 of 984 libraries in 3,219ms. +``` + + +I must say, Flutter’s hot reload is really impressive. For day to day use it won’t jump above its usual time as long as we do nice and small incremental changes. Size of the project doesn’t matter that much. + +Before I go I want to share with you my generator. Please have fun with it. I know it doesn’t look pretty but it does the job. If you have ideas about how to test other parts of the Flutter framework make a pull request or fork it! + +<GitHubEmbed repo="swavkulinski/flutter-stress-test" title="flutter-stress-test - This started as a question from one of the Flutter meet-ups." image="images/0BUpIm2yR_KoNcT82.jpg" /> diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/175RjqqMruwwRcwQVWjkmcQ.jpeg b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/175RjqqMruwwRcwQVWjkmcQ.jpeg new file mode 100644 index 0000000000..599371680f Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/175RjqqMruwwRcwQVWjkmcQ.jpeg differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/18tCVXW_vMObZzebVQdU96g.jpeg b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/18tCVXW_vMObZzebVQdU96g.jpeg new file mode 100644 index 0000000000..2da3b7dda5 Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/18tCVXW_vMObZzebVQdU96g.jpeg differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/19XMAAu_JYPQswvTcMRhNiQ.jpeg b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/19XMAAu_JYPQswvTcMRhNiQ.jpeg new file mode 100644 index 0000000000..bd1c5fdae4 Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/19XMAAu_JYPQswvTcMRhNiQ.jpeg differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1B4G1_W-d0YsF_MWmqE6AvQ.jpeg b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1B4G1_W-d0YsF_MWmqE6AvQ.jpeg new file mode 100644 index 0000000000..f465970524 Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1B4G1_W-d0YsF_MWmqE6AvQ.jpeg differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1FOxGHPVfUKaibqcK0FSO2g.jpeg b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1FOxGHPVfUKaibqcK0FSO2g.jpeg new file mode 100644 index 0000000000..2e62d66854 Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1FOxGHPVfUKaibqcK0FSO2g.jpeg differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1HIkUhLDGGJknOoFb8AC2GQ.jpeg b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1HIkUhLDGGJknOoFb8AC2GQ.jpeg new file mode 100644 index 0000000000..332b2dce59 Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1HIkUhLDGGJknOoFb8AC2GQ.jpeg differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1JTvcXKm4OadZ9FjHAyjAzg.jpeg b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1JTvcXKm4OadZ9FjHAyjAzg.jpeg new file mode 100644 index 0000000000..bbe2f99d2b Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1JTvcXKm4OadZ9FjHAyjAzg.jpeg differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1MdrcuqmMZPxXdrkJbDAdIA.webp b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1MdrcuqmMZPxXdrkJbDAdIA.webp new file mode 100644 index 0000000000..c13030c7e3 Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1MdrcuqmMZPxXdrkJbDAdIA.webp differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1TIrEkPNew4nRhnOX_JZeKw.webp b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1TIrEkPNew4nRhnOX_JZeKw.webp new file mode 100644 index 0000000000..52537862f6 Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1TIrEkPNew4nRhnOX_JZeKw.webp differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1Z5ZnnwJZgPjU71ahEdXKMg.webp b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1Z5ZnnwJZgPjU71ahEdXKMg.webp new file mode 100644 index 0000000000..0f6f8c6362 Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1Z5ZnnwJZgPjU71ahEdXKMg.webp differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1cIHVKHTVvRkPEiualLOXeQ.jpeg b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1cIHVKHTVvRkPEiualLOXeQ.jpeg new file mode 100644 index 0000000000..a1f6352559 Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1cIHVKHTVvRkPEiualLOXeQ.jpeg differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1ftJN2u294p-nycRBfkwigQ.webp b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1ftJN2u294p-nycRBfkwigQ.webp new file mode 100644 index 0000000000..5dd82ea2b4 Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1ftJN2u294p-nycRBfkwigQ.webp differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1g7OlGkj6nfHBCjiX3MNFDA.webp b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1g7OlGkj6nfHBCjiX3MNFDA.webp new file mode 100644 index 0000000000..bacf0c7591 Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1g7OlGkj6nfHBCjiX3MNFDA.webp differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1g7pI_LsKz7SMDpaDrsw1GA.jpeg b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1g7pI_LsKz7SMDpaDrsw1GA.jpeg new file mode 100644 index 0000000000..4514590d00 Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1g7pI_LsKz7SMDpaDrsw1GA.jpeg differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1mH5YXCtec8kz7iNQhNnnQQ.jpeg b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1mH5YXCtec8kz7iNQhNnnQQ.jpeg new file mode 100644 index 0000000000..57cf106d87 Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1mH5YXCtec8kz7iNQhNnnQQ.jpeg differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1qbiAd4dCqHePwPdD8nUf4g.jpeg b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1qbiAd4dCqHePwPdD8nUf4g.jpeg new file mode 100644 index 0000000000..67dd2d853a Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1qbiAd4dCqHePwPdD8nUf4g.jpeg differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1s757IVhn5sGl8hKwxtVl7A.jpeg b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1s757IVhn5sGl8hKwxtVl7A.jpeg new file mode 100644 index 0000000000..2b9d21b33b Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/images/1s757IVhn5sGl8hKwxtVl7A.jpeg differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/index.md b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/index.md new file mode 100644 index 0000000000..eaac6be587 --- /dev/null +++ b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-cloud-next/index.md @@ -0,0 +1,99 @@ +--- +title: "That’s a wrap: Everything Flutter at Google Cloud Next" +description: "Our team has been hard at work this Spring, preparing to meet over 30,000 of you in Las Vegas for Google Cloud Next. For those who couldn’t…" +publishDate: 2026-04-27 +author: twerske +image: images/1MdrcuqmMZPxXdrkJbDAdIA.webp +category: events +layout: blog +--- + +<DashImage figure src="images/1MdrcuqmMZPxXdrkJbDAdIA.webp" alt="Google Cloud Next Recap 2026" caption="Google Cloud Next Recap 2026" /> + + +## That’s a wrap: Everything Flutter at Google Cloud Next + +Our team has been hard at work this Spring, preparing to meet over 30,000 of you in Las Vegas for Google Cloud Next. For those who couldn’t make it in-person, here is a breakdown of the major highlights, announcements, and experiences from the Flutter and Dart team. + +<DashImage figure src="images/1cIHVKHTVvRkPEiualLOXeQ.jpeg" alt="Dash team at Google Cloud Next 2026" caption="Dash team at Google Cloud Next 2026" /> + + +## Big announcements + +* **Full-stack Dart:** The team announced a preview of Dart support for [Firebase Functions](https://firebase.google.com/docs/functions/start-dart). Yep, that’s right: experience using Dart for both your frontend and backend. We also introduced deeper integrations with **Firebase using the Dart Admin SDK** to reduce context switching and improve development velocity. +Check out the announcement blog, and documentation, and stay tuned for a full breakout session all about this feature at Google I/O. + +## Onsite experiences + +* **GenLatte:** We built an AI-powered specialty coffee shop in the center of Next, created with [**Flutter GenUI**](https://docs.flutter.dev/ai/genui). Attendees ordered drinks using a GenUI Flutter app, then watched baristas make a latte and print a custom nanobanana-generated image on the foam. + +<DashImage figure src="images/1HIkUhLDGGJknOoFb8AC2GQ.jpeg" alt="The GenLatte booth" caption="The GenLatte booth" /> + + +<DashImage figure src="images/1TIrEkPNew4nRhnOX_JZeKw.webp" alt="Use a Flutter app to order a latte with custom foam art!" caption="Use a Flutter app to order a latte with custom foam art!" /> + + +<DashImage figure src="images/175RjqqMruwwRcwQVWjkmcQ.jpeg" alt="Attendees enjoying their custom lattes" caption="Attendees enjoying their custom lattes" /> + + +<DashImage figure src="images/1s757IVhn5sGl8hKwxtVl7A.jpeg" alt="Kate Lovett helps Dash fulfill her latte needs" caption="Kate Lovett helps Dash fulfill her latte needs" /> + + +* **Agentic mobile & web demos galore:** The expo floor was packed with energy, including three Dart and Flutter demos, showcasing Fullstack Dart, GenUI, and a special appearance by our friends over at [VGV](https://verygood.ventures/). + +<DashImage figure src="images/18tCVXW_vMObZzebVQdU96g.jpeg" alt="Answering questions about GenUI" caption="Answering questions about GenUI" /> + + +<DashImage figure src="images/1g7pI_LsKz7SMDpaDrsw1GA.jpeg" alt="Demoing Firebase Functions for Dart" caption="Demoing Firebase Functions for Dart" /> + + +<DashImage figure src="images/1ftJN2u294p-nycRBfkwigQ.webp" alt="The Partiful app— the UI you see is generated on the fly" caption="The Partiful app— the UI you see is generated on the fly" /> + + +* **The Builder hub:** This served as the “home base” for the developer community on the Expo floor. It featured dedicated booths for **Flutter** and our friends at Firebase and Go, providing a space for developers to connect with experts and explore new tools. + +<DashImage figure src="images/1g7OlGkj6nfHBCjiX3MNFDA.webp" alt="The Builder Hub where developers can greet, meet, and repeat" caption="The Builder Hub where developers can greet, meet, and repeat" /> + + +## Key sessions & customer stories + +* **Developer keynote:** Emma Twersky hosted the Developer keynote, showcasing how Flutter is part of Google Cloud’s big bet on the future of agents. + +<DashImage figure src="images/1FOxGHPVfUKaibqcK0FSO2g.jpeg" alt="Richard Seroter and Emma Twersky giving the Developer Keynote" caption="Richard Seroter and Emma Twersky giving the Developer Keynote" /> + + +<YoutubeEmbed id="A01DQ8_xy7Q" title="Google Cloud Next '26 Developer Keynote" fullwidth="true"/> + + +* **Toyota & Talabat:** Real-world enterprise success was a major theme. Abdallah Shaban took the stage alongside industry leaders to showcase how Flutter is transforming their core products. [Toyota shared how they are revolutionizing automotive UX using Flutter](https://www.youtube.com/watch?v=A3ApoV_QRio) for their next-generation infotainment systems, while [Talabat demonstrated how they innovate faster and scale across the Middle East](https://www.youtube.com/watch?v=A3ApoV_QRio) using the framework. + +<DashImage figure src="images/1Z5ZnnwJZgPjU71ahEdXKMg.webp" alt="Toyota session" caption="Toyota session" /> + + +<DashImage figure src="images/1JTvcXKm4OadZ9FjHAyjAzg.jpeg" alt="Talabat session" caption="Talabat session" /> + + +* **Generative UI Deep Dive:** Yegor Jbanov and Andrew Brogdon led a session on how to move beyond text-based chatbots by giving your agents the power to create their own UI. +Stay tuned for Google I/O where this session will be available world wide on the Flutter YouTube channel. + +<DashImage figure src="images/1mH5YXCtec8kz7iNQhNnnQQ.jpeg" alt="GenUI session" caption="GenUI session" /> + + +* **Building full-stack Dart:** Rody Davis and Kevin Moore shared why you should be excited about today’s biggest announcement for Dart. +Stay tuned for Google I/O where this session will be available world wide on the Flutter YouTube channel. + +## Our community is thriving + +* **GDE Summit:** Held just before the main event, this summit brought together over **350 global Google Developer Experts (GDEs)** to share feedback on Flutter with the core team, including a main stage session on Flutter GenUI. + +* **Developer meetups:** Throughout the week, the **Expo Meetup Hub** hosted informal gatherings. Flutter helped host the Mobile Developers meetup, and we had a blast meeting community members from around the world and sharing why we think Flutter is the best way to build. + +<DashImage figure src="images/1qbiAd4dCqHePwPdD8nUf4g.jpeg" alt="Developer meetups at Cloud Next" caption="Developer meetups at Cloud Next" /> + + +<DashImage figure src="images/1B4G1_W-d0YsF_MWmqE6AvQ.jpeg" alt="Firebase meetup at Google Cloud Next" caption="Firebase meetup at Google Cloud Next" /> + + +And that’s a wrap! But not for long… [Register today for Google I/O 2026](https://io.google/2026/) where our team has even more planned. We can’t wait to surprise you. 😉 + +<DashImage figure src="images/19XMAAu_JYPQswvTcMRhNiQ.jpeg" alt="See you at Google I/O!" caption="See you at Google I/O!" /> diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-i-o-2026/images/1N5Jz1tLlbGEC9sU49O97IA.webp b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-i-o-2026/images/1N5Jz1tLlbGEC9sU49O97IA.webp new file mode 100644 index 0000000000..8e1abd07a8 Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-i-o-2026/images/1N5Jz1tLlbGEC9sU49O97IA.webp differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-i-o-2026/images/1URQ_4vTl-Zf47WKBM_I8sg.webp b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-i-o-2026/images/1URQ_4vTl-Zf47WKBM_I8sg.webp new file mode 100644 index 0000000000..72faec2ea7 Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-i-o-2026/images/1URQ_4vTl-Zf47WKBM_I8sg.webp differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-i-o-2026/images/1np8TNr84DxJv7bO_nx_h8g.jpeg b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-i-o-2026/images/1np8TNr84DxJv7bO_nx_h8g.jpeg new file mode 100644 index 0000000000..a0faea75f7 Binary files /dev/null and b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-i-o-2026/images/1np8TNr84DxJv7bO_nx_h8g.jpeg differ diff --git a/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-i-o-2026/index.md b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-i-o-2026/index.md new file mode 100644 index 0000000000..03d06775bd --- /dev/null +++ b/sites/www/content/blog/thats-a-wrap-everything-flutter-at-google-i-o-2026/index.md @@ -0,0 +1,106 @@ +--- +title: "That’s a wrap: Everything Flutter at Google I/O 2026" +description: "What a week! Google I/O 2026 is officially in the books. It’s time to recap the whirlwind of updates for the Flutter and Dart communities." +publishDate: 2026-05-28 +author: twerske +image: images/1N5Jz1tLlbGEC9sU49O97IA.webp +category: events +layout: blog +--- + +<DashImage figure src="images/1N5Jz1tLlbGEC9sU49O97IA.webp" alt="Flutter recap at Google I/O 2026!" caption="Flutter recap at Google I/O 2026!" /> + + +## That’s a wrap: Everything Flutter at Google I/O 2026 + +What a week! Google I/O 2026 is officially in the books. It’s time to recap the whirlwind of updates for the Flutter and Dart communities. + +<YoutubeEmbed id="vNwCw6uVyTg" title="Flutter at Google I/O 2026 in 5 minutes" fullwidth="true"/> + + +The onsite star was a full-sized specialty coffee shop in the absolute center of Google I/O, built entirely with **Flutter GenUI**, where nanobanana-generated artwork was printed right on the coffee foam. + +<DashImage figure src="images/1URQ_4vTl-Zf47WKBM_I8sg.webp" alt="Just a few generated foam coffee art lattes at I/O" caption="Just a few generated foam coffee art lattes at I/O" /> + + +But don’t worry if you weren’t able to make it to Shoreline. We recorded every moment and more, now available for on-demand viewing on the Flutter YouTube channel. + +## 📺 Watch every session + +From keynote to tech session, we can’t wait for you to see every moment of Flutter from I/O 2026. + +## 🌟 What’s new in Flutter + +<YoutubeEmbed id="I1uIbGh1dGE" title="What's new in Flutter" fullwidth="true"/> + + +A few of the highlights: + +* **Flutter 3.44 & Dart 3.12:** These releases brought major advancements with Agentic Hot Reload, GenUI with a showcase from Li-Te Cheng on the DeepMind team, Toyota has embedded Flutter in the 2026 RAV4, improved rendering performance with Impeller, decoupling of Material and Cupertino UI from the framework, Swift Package Manager is on by default for iOS and macOS, Canonical now leads the Flutter desktop roadmap, and significant updates to platform-specific fidelity with HCPP. + +## 🌟 Vibe once, run anywhere with Antigravity and Flutter + +<YoutubeEmbed id="UNdQhnpm8GY" title="Vibe once, run anywhere with Google Antigravity and Flutter" fullwidth="true"/> + + +* **What you’ll learn:** A technical introduction to **Antigravity**, a new IDE focused on building with agents. You will learn how to leverage “vibe-based” development to create experiences that adapt and run across all platforms seamlessly. + +* **Why it matters:** It provides the architectural roadmap for shifting from “hard-coded” layouts to “vibe-based” generative experiences, allowing your app to adapt to user needs on the fly. + +## How to write really good Flutter code + +<YoutubeEmbed id="O0UjId1VoRU" title="How to write really good Flutter code" fullwidth="true"/> + + +* **What you’ll learn:** Principles for writing high-quality, maintainable Flutter code, and how modern tooling — including Widget Previews, DevTools, MCP servers, and Skills make it easier than ever to build great apps. + +* **Why it matters:** AI can write code faster than ever, but best practices and robust tooling provide the quality control needed to keep Flutter applications maintainable over time. + +## Introducing the Full-stack developer series + +<YoutubeEmbed id="hNbY3Bmgojg" title="Introducing the Full-stack developer guide (Flutter, Firebase, Angular)" fullwidth="true"/> + + +* **What you’ll learn:** Not a lot, but this less-than-two-minute video is fun and introduces the four episode series. + +## Full-stack dev series #1 — Flutter + A2UI = GenUI + +<YoutubeEmbed id="tXeyaV1gVJk" title="Flutter + A2UI = GenUI" fullwidth="true"/> + + +* **What you’ll learn:** A technical deep dive into the building blocks of Generative UI, with a top-to-bottom conversion of a text-based chatbot into an agent that creates its own UI and understands how you interact with it. + +* **Why it matters:** Not only can GenUI turn a slow, text-based experience into a delightful one, but it opens up new user experience patterns in which static designs and route hierarchies become flexible, on-demand UI tailored to individual users and their goals. + +## Full-stack dev series #2 — Full-stack Dart + +<YoutubeEmbed id="VXFDkg96f4I" title="How to unify your app's tech stack with Full-stack Dart and Firebase Functions" fullwidth="true"/> + + +* **What you’ll learn:** How to unify your app’s tech stack and eliminate context switching by using Dart for both frontend and backend logic. This video builds a complete serverless application live, using **Dart for Firebase Cloud Functions**. + +* **Why it matters:** This approach improves development velocity and reduces costs by allowing teams to share logic, tooling, and expertise across the entire application stack. + +## Full-stack dev series #3 — Introducing Genkit Dart + +<YoutubeEmbed id="QO30-W3b_vw" title="Introducing Genkit Dart" fullwidth="true"/> + + +* **What you’ll learn:** How to create Genkit flows that power your app’s backend and frontend, and discover the essential security steps needed to protect your AI endpoints. This video covers everything from initial setup to implementing rate limits and caching to keep your project secure and cost-effective. + +* **Why it matters:** Genkit bridges the gap between prototyping and shipping reliable AI applications. It simplifies building AI into existing code by providing a unified SDK across multiple languages and models, alongside robust debugging and monitoring tools. Genkit works as a backend, but also in your Flutter app! + +## Full-stack dev series #4 — Everything you don’t know about building great native apps with Flutter + +<YoutubeEmbed id="2z7U6GU7QwQ" title="Everything you don't know about building great native apps with Flutter" fullwidth="true"/> + + +* **What you’ll learn:** All the small additions we’ve made to Flutter to make a compelling native experience, from embracing native workflows, supporting new languages for Dart binding generation, or new rendering methods that balance the needs of host UIs and Flutter + +* **Why it matters:** Flutter is not about finding a “lowest common denominator.” It is about providing a high-fidelity, high-performance framework that respects and enhances the native platforms it runs on. + +…and that’s it! + +Thank you all for joining along as we brewed up one of our favorite I/Os yet. Stay tuned for more blog posts, videos and docs about everything we announced, and [all of the community events](http://flutter.dev/events) we’re attending starting next month. + +<DashImage figure src="images/1np8TNr84DxJv7bO_nx_h8g.jpeg" alt="Farewell from Google I/O 2026!" caption="Farewell from Google I/O 2026!" /> diff --git a/sites/www/content/blog/the-flutter-create-winners-are/images/09r31IUKmyYJdBwX0.webp b/sites/www/content/blog/the-flutter-create-winners-are/images/09r31IUKmyYJdBwX0.webp new file mode 100644 index 0000000000..3ceb4d3d5b Binary files /dev/null and b/sites/www/content/blog/the-flutter-create-winners-are/images/09r31IUKmyYJdBwX0.webp differ diff --git a/sites/www/content/blog/the-flutter-create-winners-are/images/0PfWbKHdiDVCfn3b-.webp b/sites/www/content/blog/the-flutter-create-winners-are/images/0PfWbKHdiDVCfn3b-.webp new file mode 100644 index 0000000000..2426a535ff Binary files /dev/null and b/sites/www/content/blog/the-flutter-create-winners-are/images/0PfWbKHdiDVCfn3b-.webp differ diff --git a/sites/www/content/blog/the-flutter-create-winners-are/images/0bsrTVoVwYi38mp0m.webp b/sites/www/content/blog/the-flutter-create-winners-are/images/0bsrTVoVwYi38mp0m.webp new file mode 100644 index 0000000000..fc3186a191 Binary files /dev/null and b/sites/www/content/blog/the-flutter-create-winners-are/images/0bsrTVoVwYi38mp0m.webp differ diff --git a/sites/www/content/blog/the-flutter-create-winners-are/images/0lMe2G_2Al68HniQ6.gif b/sites/www/content/blog/the-flutter-create-winners-are/images/0lMe2G_2Al68HniQ6.gif new file mode 100644 index 0000000000..6fbd94f349 Binary files /dev/null and b/sites/www/content/blog/the-flutter-create-winners-are/images/0lMe2G_2Al68HniQ6.gif differ diff --git a/sites/www/content/blog/the-flutter-create-winners-are/images/0peT4RsFqjTeCAeBn.webp b/sites/www/content/blog/the-flutter-create-winners-are/images/0peT4RsFqjTeCAeBn.webp new file mode 100644 index 0000000000..cf46e1b079 Binary files /dev/null and b/sites/www/content/blog/the-flutter-create-winners-are/images/0peT4RsFqjTeCAeBn.webp differ diff --git a/sites/www/content/blog/the-flutter-create-winners-are/images/1q-DyfLgHgeJRsrEsm9wOVw.jpeg b/sites/www/content/blog/the-flutter-create-winners-are/images/1q-DyfLgHgeJRsrEsm9wOVw.jpeg new file mode 100644 index 0000000000..c4f2d58cc8 Binary files /dev/null and b/sites/www/content/blog/the-flutter-create-winners-are/images/1q-DyfLgHgeJRsrEsm9wOVw.jpeg differ diff --git a/sites/www/content/blog/the-flutter-create-winners-are/index.md b/sites/www/content/blog/the-flutter-create-winners-are/index.md new file mode 100644 index 0000000000..34fea758ab --- /dev/null +++ b/sites/www/content/blog/the-flutter-create-winners-are/index.md @@ -0,0 +1,97 @@ +--- +title: "The Flutter Create winners are…" +description: "Flutter Create is a contest that challenges you to build something interesting, inspiring, and beautiful with Flutter using 5KB or less of…" +publishDate: 2019-05-07 +author: aguinis +image: images/1q-DyfLgHgeJRsrEsm9wOVw.jpeg +category: events +layout: blog +--- + +[Flutter Create](http://flutter.dev/create) is a contest that challenges you to build something interesting, inspiring, and beautiful with Flutter using 5KB or less of Dart code. + +We received over 750 unique submissions from 60+ countries and regions, and included entries from first time coders to Flutter experts. This video provides a glimpse of what you made from the hundreds of submissions we received: + +<YoutubeEmbed id="WLvpROMUjYQ" title="Flutter Create Highlight Reel" fullwidth="true"/> + + +To put the limited size in perspective, 5KB is… + +* **Less than half a second of a typical music file.** A typical MP3 file is encoded at 128kbps (kilobits/sec). That’s 128/8 = 16 kilobytes / second. So 5KB is 0.3125s of a typical MP3 file. + +* **Less than 0.05% of the size of the [HTML specification](https://html.spec.whatwg.org/).** Less than 5% of the size of the HTML specification’s [table of contents](https://html.spec.whatwg.org/multipage/), even. + +* **[The size of a 80x60 image](https://drive.google.com/file/d/1qnjyrBhGrx07CygxmIq6NJ8tZb6lK86q/view?usp=sharing), encoded in GIF format.** [Original image](https://drive.google.com/file/d/1Krxrdn9Q_GDzfLWBmDnu4f9Pyk83DpY8/view). + +We hope you had fun using [Flutter](http://flutter.dev), and participating in the inaugural [Flutter Create contest](http://flutter.dev/create). + +## The winners are… 🥁🥁🥁 + +## Grand Prize Winner: Compass by Zebiao Hu + +<DashImage figure src="images/09r31IUKmyYJdBwX0.webp" /> + + +[This app](https://github.com/zesage/flutter_compass) is a polished, custom-designed fully functioning compass. Once you scroll up, it displays a globe that users can rotate and get real-time weather data wherever the red dot is placed. + +## Visual Beauty Winner: Relax by Erin Morrissey + +<DashImage figure src="images/0PfWbKHdiDVCfn3b-.webp" /> + + +[Relax](https://github.com/erinmorrissey/relax) is a beautiful audio meditation app that displays imagery of rain, forest, sunset, and ocean with corresponding sounds. + +## Code Quality Winner: Pocket Piano by Rody Davis + +<DashImage figure src="images/0bsrTVoVwYi38mp0m.webp" /> + + +A piano built for both iOS and Android. You can change the octave (by sliding to the left or right), and key width, toggle labels on the keys, and play any note (or notes) by tapping. The piano also fully supports semantics for screen reader users. [Check out their tutorial](https://rodydavisjr.com/2019/03/12/making-a-piano/) to build the app yourself. + +## Overall Execution Winner: TimeFlow by Fabian Stein + +<DashImage figure src="images/0lMe2G_2Al68HniQ6.gif" /> + + +[TimeFlow](https://github.com/Fabian-Stein/timeflow) is a timer that displays a sphere that shatters into pieces and slowly reassembles in one, two, or three minutes, as selected (reassembly not shown in the video). This gentle approach to a timer addresses the complaint that numeric timers can stress the meditator. + +## Novelty of Idea Winner: Flutter Events by Noel Jacob + +<DashImage figure src="images/0peT4RsFqjTeCAeBn.webp" /> + + +[This app](https://github.com/noejac123/Flutter-Event) allows event calendars to be encoded in QR codes so that attendees can quickly see what is happening at their event. It is impressive how much of an application’s UI can be stored in a JSON file (in a QR code) rather than being hard-coded in the application. + +## **Other Submissions** + +Judging was no small task, and our judges found it difficult to choose a mere 5 winners out of all the entries based on our criteria. Therefore, our judges also awarded honorable mentions to the following entries, in alphabetical order: + +* [**Elements app**](https://github.com/brianrobles204/Elements-App) by Brian Carlos Robles + +* [**Fauth**](https://github.com/redsolver/fauth) by Christian Pietzuch + +* [**Filipino cuisine**](https://github.com/markgrancapal/filipino_cuisine) by John Mark Grancapal + +* [**Find aik**](https://github.com/kroppli/find_aik) by Igor Petushkov + +* [**Flutty Loops**](https://github.com/rxlabz/flutty_loops/) by Erick Ghaumez + +* [**Foxgame**](https://github.com/KnockaertSven/foxgame) by Sven Knockaert + +* [**Lights out**](https://github.com/KhaleelSH/lights_out) by Khaleel Shaheen + +* [**Kitten Quartet**](https://github.com/chenzhenxi/kitten-quartet) by Zhenxi Chen + +* [**Pomodoro**](https://github.com/Alameen688/pomodoro) by Al-Ameen Ogundiran + +* [**Timelog**](https://github.com/giall/timelog) by Christos Giallouros + +* [**Touch the programming**](https://github.com/tnantoka/touch_the_programming) by Tatsuya Tobioka + +We were blown away by the quality and creativity of the entries. Some developers took time-off work to focus on their apps, and one developer turned their entry into a full-time job. + +To see more examples of many of the hundreds of projects that were submitted, see the [FutterCreateSubmissions2019 repo](https://github.com/pinkeshdarji/FlutterCreateSubmissions2019) on GitHub. + +Thanks so much for participating! Let us know what you thought about the contest. We may offer [Flutter Create](https://medium.com/flutter-io/flutter-create-is-live-with-over-10-000-in-prizes-1a98c5642baa), or a variation, again in the future. + +<DashImage figure src="images/1q-DyfLgHgeJRsrEsm9wOVw.jpeg" /> diff --git a/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/04Mb0dUry2lkuUvXI.webp b/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/04Mb0dUry2lkuUvXI.webp new file mode 100644 index 0000000000..30f68b2748 Binary files /dev/null and b/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/04Mb0dUry2lkuUvXI.webp differ diff --git a/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/0MUGkFV9MRrcT7-68.webp b/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/0MUGkFV9MRrcT7-68.webp new file mode 100644 index 0000000000..8faed7740c Binary files /dev/null and b/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/0MUGkFV9MRrcT7-68.webp differ diff --git a/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/0YnEEnzt7QIuuxofn.webp b/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/0YnEEnzt7QIuuxofn.webp new file mode 100644 index 0000000000..fcdee84fd5 Binary files /dev/null and b/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/0YnEEnzt7QIuuxofn.webp differ diff --git a/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/14QurT4QkFUMSNCSFtfOzxg.webp b/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/14QurT4QkFUMSNCSFtfOzxg.webp new file mode 100644 index 0000000000..ec40193ad3 Binary files /dev/null and b/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/14QurT4QkFUMSNCSFtfOzxg.webp differ diff --git a/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/1K2e5QrjXtv0bCU35H_uRkw.webp b/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/1K2e5QrjXtv0bCU35H_uRkw.webp new file mode 100644 index 0000000000..89739f8eb9 Binary files /dev/null and b/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/1K2e5QrjXtv0bCU35H_uRkw.webp differ diff --git a/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/1OGs9_siPcMAYd1yDAp-AOA.webp b/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/1OGs9_siPcMAYd1yDAp-AOA.webp new file mode 100644 index 0000000000..fabb6fc9e1 Binary files /dev/null and b/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/1OGs9_siPcMAYd1yDAp-AOA.webp differ diff --git a/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/1qYEQdMvlHvAFS1biiH6phg.webp b/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/1qYEQdMvlHvAFS1biiH6phg.webp new file mode 100644 index 0000000000..0caeca8fc3 Binary files /dev/null and b/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/1qYEQdMvlHvAFS1biiH6phg.webp differ diff --git a/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/1rBWjG-4wCqJlY98oPeADrw.webp b/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/1rBWjG-4wCqJlY98oPeADrw.webp new file mode 100644 index 0000000000..56389d6c59 Binary files /dev/null and b/sites/www/content/blog/the-future-of-ios-development-with-flutter/images/1rBWjG-4wCqJlY98oPeADrw.webp differ diff --git a/sites/www/content/blog/the-future-of-ios-development-with-flutter/index.md b/sites/www/content/blog/the-future-of-ios-development-with-flutter/index.md new file mode 100644 index 0000000000..779bc49e10 --- /dev/null +++ b/sites/www/content/blog/the-future-of-ios-development-with-flutter/index.md @@ -0,0 +1,116 @@ +--- +title: "The future of iOS development with Flutter" +description: "Our recent accomplishments and future priorities for making Flutter a better tool for developing iOS apps" +publishDate: 2023-05-18 +author: leighajarett +image: images/1OGs9_siPcMAYd1yDAp-AOA.webp +category: announcements +layout: blog +--- + +Since its launch in 2017, [Flutter](http://flutter.dev) has quickly become a top choice for iOS app development. Developers worldwide *love* Flutter. Using Flutter, they can code once and deploy their apps to iOS, Android, web and desktop. This has led to a massive following, with over a[ ****million apps**](https://medium.com/flutter/racing-forward-at-i-o-2023-with-flutter-and-dart-df2a8fa841ab) **built using Flutter. + +For iOS developers who value innovative design, Flutter has become a go-to platform. By providing a blank canvas, Flutter gives designers endless possibilities. They can create original experiences that break free from pre-set design systems. + +<YoutubeEmbed id="6IKhXXFFOuw" title="Build Wonders with Flutter" fullwidth="true"/> + + +Today, I want to tell you about some recent improvements to Flutter and our future focus areas for iOS developers. If you’re a pro or new to Flutter, read on to learn about the future of Flutter for iOS. + +## Notable Flutter iOS apps + +Flutter has seen success across various industries and categories for iOS development. Large enterprises like [BMW](https://apps.apple.com/us/app/my-bmw/id1519457734), [Sonos](https://apps.apple.com/us/app/sonos/id1488977981), and [Nubank](https://apps.apple.com/us/app/nubank-conta-e-cart%C3%A3o/id814456780) build their digital experiences with Flutter. + +<DashImage figure src="images/1OGs9_siPcMAYd1yDAp-AOA.webp" /> + + +Tech giants like [WeChat](https://apps.apple.com/us/app/wechat/id414478124) and [PUBG MOBILE](https://www.youtube.com/watch?v=03ETVuWZiBc) use Flutter to support over one billion active users. Smaller companies have also capitalized on Flutter’s quick development cycle. One example would be [BrickIt](https://apps.apple.com/us/app/brickit-app/id1477221636) which uses machine learning to suggest new LEGO brick creations. + +Apple and other tech leaders have even recognized Flutter iOS apps. [Wonderous](http://wonderous.app), Flutter’s reference app, was [nominated for a Webby in design](https://winners.webbyawards.com/2023/apps-dapps-and-software/app-features/best-user-interface/239934/wonderous). Apple accepted [Reflection.app](https://flutter.dev/showcase/reflectionapp) to its prestigious accelerator program. [So Vegan](https://flutter.dev/showcase/so-vegan) was awarded Apple’s App of the Day *several* times. + +<DashImage figure src="images/14QurT4QkFUMSNCSFtfOzxg.webp" /> + + +## Recent enhancements + +Flutter’s iOS support has been a top priority. We’ve made significant improvements in recent releases. Let’s review some highlights: + +### **Improved Performance** + +Impeller, our purpose-built rendering engine, now serves as the default for Flutter on iOS. Impeller has been a multi-year journey for our team. It addresses the top concerns for iOS developers leveraging Flutter: smooth graphics and top tier performance. Since its release, we’ve seen great improvements in app quality. More recently, enhancements to Flutter’s engine continue to improve startup latency and reduce app size. + +<YoutubeEmbed id="vd5NqS01rlA" title="Introducing Impeller - Flutter's new rendering engine" fullwidth="true"/> + + +Impeller also simplifies adding new features. These features range from wide gamut image support to custom rendering solutions. We demonstrated one such custom rendering solution, a proof of concept for 3D support, at [Flutter Forward.](https://www.youtube.com/watch?v=goL7tvLQ7Dw) + +<DashImage figure src="images/0MUGkFV9MRrcT7-68.webp" /> + + +<DashImage figure src="images/0YnEEnzt7QIuuxofn.webp" /> + + +<DashImage figure src="images/04Mb0dUry2lkuUvXI.webp" alt="Impeller enables Flutter to render 3D graphics, as shown in this fun demo of Dash (our mascot)." caption="Impeller enables Flutter to render 3D graphics, as shown in this fun demo of Dash (our mascot)." /> + + +### **Developer Experience Enhancements** + +We understand the challenges of building and releasing an iOS app. To save help save developer’s time, we’ve added new tools and resources to simplify the entire process. You can now [connect to iOS devices](https://docs.flutter.dev/get-started/install/macos#ios-setup) over Wi-Fi to test and debug your app. We’ve also[ added validation](https://github.com/flutter/flutter/issues/97730) to the release process. This step ensures that you took all necessary steps before releasing your app to the App Store. + +<iframe src="https://twitter.com/EnzoConty/status/1656379609796038670" width="500" height="281" frameborder="0" allowfullscreen></iframe> + + +In addition, we’ve created iOS-focused documentation and tutorials to help developers learn Flutter and build iOS apps. We’ve incorporated Swift and SwiftUI examples into our documentation. We added guides on moving from[ Swift to Dart](https://dart.dev/guides/language/coming-from/swift-to-dart), [SwiftUI to Flutter](https://docs.flutter.dev/get-started/flutter-for/swiftui-devs) and adding[ Flutter to an existing iOS app](https://docs.flutter.dev/add-to-app/ios/project-setup). We’ve also added resources for supporting [flavors on iOS](https://docs.flutter.dev/deployment/flavors) and using[ iOS app extensions](https://docs.flutter.dev/platform-integration/ios/app-extensions) like home and lock screen widgets. + +### **iOS-style UI ComponentUpdates** + +The [Cupertino library](https://docs.flutter.dev/ui/widgets/cupertino) provides widgets (UI components) resembling SwiftUI and UIKit views. This should make your app feel right at home on Apple devices. To better align with the latest iOS design guidelines, we’ve made significant updates to our Cupertino library. + +We’ve addressed top-voted issues and added new widgets like [CupertinoCheckbox](https://api.flutter.dev/flutter/cupertino/CupertinoCheckbox-class.html), [CupertinoRadio](https://api.flutter.dev/flutter/cupertino/CupertinoRadio-class.html), [CupertinoListTile](https://api.flutter.dev/flutter/cupertino/CupertinoListTile-class.html) and spell check support on text input fields. + +<DashImage figure src="images/1qYEQdMvlHvAFS1biiH6phg.webp" alt="Automatic iOS style spell check on TextField and CupertinoTextField" caption="Automatic iOS style spell check on TextField and CupertinoTextField" /> + + +In addition, we recognize the importance of cross-platform design. We’ve added [adaptive constructors](https://docs.flutter.dev/resources/platform-adaptations#ui-components) to several Material widgets. This allows your Flutter app to adapt between Android and iOS design conventions. For common widgets *without* adaptive constructors, we’ve created [preliminary guides](https://docs.flutter.dev/resources/platform-adaptations#top-app-bar-and-navigation-bar) to give you code snippets to adapt entire widgets or widget properties. + +<DashImage figure src="images/1rBWjG-4wCqJlY98oPeADrw.webp" /> + + +## *Roadmap* + +As we continue to make Flutter a better tool for iOS developers, we’re focusing on a few key areas: + +### **Integrations with the Apple ecosystem** + +Flutter developers should be able to easily use Apple’s APIs in their apps. This allows them to create immersive experiences that integrate all of Apple’s great offerings. + +A few months ago, we launched [FFIgen](https://pub.dev/packages/ffigen). This tool generates bindings to [call Objective-C and Swift APIs directly from Dart code.](https://dart.dev/guides/libraries/objective-c-interop) Some apps are using FFIgen to call Apple’s APIs now. But, there are still limitations. We’re actively working on making the tool more robust with support for async callbacks and better Swift interop. + +Furthermore, we know the importance of [app extensions](https://developer.apple.com/app-extensions/) within the iOS ecosystem. That’s why we’re developing a way to create the user interface (UI) for some extensions using Flutter. This will enable developers to reuse components from their Flutter app to design their extension’s interface. It’s worth noting that this approach won’t work for all types of extensions. WidgetKit widgets, for instance, have strict API restrictions. However, it will work for other common extensions such as [Share](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/Share.html#//apple_ref/doc/uid/TP40014214-CH12-SW1) or [iMessage](https://developer.apple.com/documentation/messages). To keep up with our progress, you can follow [this work in this Flutter Github issue](https://github.com/flutter/flutter/issues/124287). + +<DashImage figure src="images/1K2e5QrjXtv0bCU35H_uRkw.webp" alt="A proof of concept for a Flutter app running as an iOS share extension" caption="A proof of concept for a Flutter app running as an iOS share extension" /> + + +### **Cross-platform design** + +Deploying your app across multiple platforms requires some design considerations. There’s a balance between customizing the design of UI components to match your brand, while adhering to platform conventions. + +As such, we’re investigating ways to improve the experience for designing and building Flutter apps. First, we want to make Flutter’s out-of-the-box UI components more flexible. This way, developers can quickly customize widgets to match their own design systems. Second, we want to leverage more automated adaptations that make Flutter apps feel natural on both iOS and Android. + +### **Developer experience** + +We’re committed to improving the iOS developer experience with Flutter. One key focus is decreasing build times to improve developer productivity. Additionally, we’re addressing long-standing requests such as the ability to [rename the Runner app](https://github.com/flutter/flutter/issues/9767). Finally, we’ll continue to prioritize performance enhancements and general iOS fidelity. + +## Send us your feedback + +As an open source project, you can track our progress on [GitHub](https://github.com/flutter/flutter). We may add, prolong, or defer features based on what we learn and what you tell us. + +As always, we value your feedback and appreciate your support! The best way to send us your input is by filing issues in [GitHub](http://github.com/flutter/flutter/issues) and filling out our quarterly user survey. You can stay up to date on future Flutter iOS work by following me on [Twitter](https://twitter.com/LeighaJarett). + +## Resources + +* [Flutter website](http://flutter.dev) + +* [Wonderous app GitHub](https://github.com/gskinnerTeam/flutter-wonderous-app) + +* [Flutter GitHub](https://github.com/flutter) \ No newline at end of file diff --git a/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/images/0-BQ6W3Ke-W_JQiX4.gif b/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/images/0-BQ6W3Ke-W_JQiX4.gif new file mode 100644 index 0000000000..c6684178ce Binary files /dev/null and b/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/images/0-BQ6W3Ke-W_JQiX4.gif differ diff --git a/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/images/096VXf5cl-KdkF0st.webp b/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/images/096VXf5cl-KdkF0st.webp new file mode 100644 index 0000000000..2a96fdab0d Binary files /dev/null and b/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/images/096VXf5cl-KdkF0st.webp differ diff --git a/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/images/0IjMrne-3AEcMAkUP.webp b/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/images/0IjMrne-3AEcMAkUP.webp new file mode 100644 index 0000000000..e584c5cb10 Binary files /dev/null and b/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/images/0IjMrne-3AEcMAkUP.webp differ diff --git a/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/images/0Ut-Uqv6H9ypaumF2.webp b/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/images/0Ut-Uqv6H9ypaumF2.webp new file mode 100644 index 0000000000..4b5d158bcf Binary files /dev/null and b/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/images/0Ut-Uqv6H9ypaumF2.webp differ diff --git a/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/images/0VQ-yBPvchbC0k_tX.webp b/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/images/0VQ-yBPvchbC0k_tX.webp new file mode 100644 index 0000000000..8f9fd8aea8 Binary files /dev/null and b/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/images/0VQ-yBPvchbC0k_tX.webp differ diff --git a/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/images/16hQYJrDKEE1r961qkxrMwQ.jpeg b/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/images/16hQYJrDKEE1r961qkxrMwQ.jpeg new file mode 100644 index 0000000000..7ed0feed14 Binary files /dev/null and b/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/images/16hQYJrDKEE1r961qkxrMwQ.jpeg differ diff --git a/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/index.md b/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/index.md new file mode 100644 index 0000000000..40c024b243 --- /dev/null +++ b/sites/www/content/blog/the-power-of-the-flutters-transform-and-gesturedetector-widgets/index.md @@ -0,0 +1,57 @@ +--- +title: "The Power of the Flutter’s Transform and GestureDetector Widgets" +description: "When it first clicked for me that Flutter gives me full control of everything on the screen, right down to the last pixel, I immediately…" +publishDate: 2019-06-13 +author: justinmc +image: images/16hQYJrDKEE1r961qkxrMwQ.jpeg +category: announcements +layout: blog +--- + +<DashImage figure src="images/16hQYJrDKEE1r961qkxrMwQ.jpeg" /> + + +When it first clicked for me that Flutter gives me full control of everything on the screen, right down to the last pixel, I immediately wanted to go beyond the fixed and predictable world of most app UIs. The fully custom widgets that I could draw using [CustomPaint](https://api.flutter.dev/flutter/widgets/CustomPaint-class.html) begged to be broken out of the typical ScrollView or Container. I wanted to move around, zoom in on things, and navigate the world that Flutter makes it possible to create. The [Transform](https://api.flutter.dev/flutter/widgets/Transform-class.html) widget was my vehicle for navigating this world, and [GestureDetector](https://api.flutter.dev/flutter/widgets/GestureDetector-class.html) provided the controls. + +<DashImage figure src="images/096VXf5cl-KdkF0st.webp" /> + + +GestureDetector gives easy access to most gestures, and Transform provides the ability to modify the perspective in which a child widget is seen. Both are simple, independent widgets that follow Flutter’s pattern of composability. When we combine the two, every gesture on a widget becomes a way to explore. + +## An introduction to transformation + +Despite its power, all that the Transform widget really does is to take a transformation matrix and apply it to its child. This results in the translation, scale, rotation, and even skew of the child with respect to the parent, all specified by one simple [Matrix4](https://api.flutter.dev/flutter/vector_math/Matrix4-class.html) object. + +Matrices are very convenient to work with when doing transformations because they are composable. Storing separate translation, scale, rotation, and skew parameters is intuitive at first, but it’s easy to run into ambiguity with things like order. + +Imagine the user performs a series of gestures back to back. If the gestures are simply a series of drags, then we can easily store the final transformation as the resulting location Offset. However, if the user drags, then rotates at the new location, and repeats this several times, how do we keep track of the final state? An offset and a double of radians is not enough, not even if we include a focal point as well. We would have to keep track of an ever expanding list of operations that the user has performed and iterate the whole thing just to get to the final state. + +A transformation matrix can be combined with any number of other transformation matrices indefinitely, and the result is always another single matrix of the same size. The GPU is very fast at doing this sort of math, even massively in parallel, such as for every pixel on the screen. This makes it great for storing the state of a widget like Transform, and the result is a very straightforward widget for Flutter developers to use. + +<DashImage figure src="images/0VQ-yBPvchbC0k_tX.webp" /> + + +## Detecting gestures + +If we tie some user gestures into updating the transformation matrix using a GestureDetector, then the user has the freedom to navigate the scene that we’re displaying to them. GestureDetector provides easy access to gestures like drag, which we can use for translation, pinch, for zooming, and even a two-finger rotation. + +<DashImage figure src="images/0IjMrne-3AEcMAkUP.webp" /> + + +<DashImage figure src="images/0Ut-Uqv6H9ypaumF2.webp" /> + + +<DashImage figure src="images/0-BQ6W3Ke-W_JQiX4.gif" /> + + +GestureDetector provides lots of different callbacks for various gestures and their start, update, and end states. However, if you’re implementing something like the pictured demo that combines several, the scale callbacks are all you’ll need. The onScaleUpdate callback provides a focalPoint, both horizontal and vertical scales, and rotation, all in one. This allows you to respond to multiple gestures at the same time, such as if the user is rotating and scaling in one gesture. See the [ScaleUpdateDetails](https://api.flutter.dev/flutter/gestures/ScaleUpdateDetails-class.html) for everything provided in the onScaleUpdate callback. + +Another thing that is often implemented in physical transformations like these is inertia. After the user finishes a gesture, they might expect the transformation to continue for awhile before slowly coming to a stop. Flutter provides a “velocity” parameter in the ending callbacks of all of these gestures, including scale. As you might guess with physics-based movement, this value is larger if the user is moving their finger quickly and smaller if the user is making a slow gesture. + +You can create this inertia effect with another great piece of Flutter infrastructure, the [Tween](https://api.flutter.dev/flutter/animation/Tween-class.html) animation. Using the velocity and position at the end of a gesture, and a basic knowledge of physics, you can come up with a final position and duration to provide to the Tween, and let it handle everything else. Flutter even includes a [friction simulation class](https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/physics/friction_simulation.dart) to handle some of these calculations for you and make things feel very realistic. + +## Conclusion + +Combining Transform and GestureDetector gives us an incredibly powerful tool. So much is possible, from moving around a game board, as shown in the screenshots, to navigating a map, to providing an interactive image viewer, and beyond. + +The app shown here is currently available as a demo in the [Flutter Gallery app](https://play.google.com/store/apps/details?id=io.flutter.demo.gallery&hl=en_US). All of the code is a part of the main flutter open source repository, and can be found in the [transformation demo source](https://github.com/flutter/flutter/blob/master/examples/flutter_gallery/lib/demo/transformations/transformations_demo.dart). Most of the transformation logic is in the [GestureTransformable](https://github.com/flutter/flutter/blob/master/examples/flutter_gallery/lib/demo/transformations/transformations_demo_gesture_transformable.dart) class, including the [inertia animation](https://github.com/flutter/flutter/blob/9d724d4c4483b585dfd4bfd719844802f8b38abe/examples/flutter_gallery/lib/demo/transformations/transformations_demo_gesture_transformable.dart#L503). \ No newline at end of file diff --git a/sites/www/content/blog/the-power-of-webviews-in-flutter/images/1W_1b-8uIxDeFKplKkxtY0g.gif b/sites/www/content/blog/the-power-of-webviews-in-flutter/images/1W_1b-8uIxDeFKplKkxtY0g.gif new file mode 100644 index 0000000000..7f77716dfd Binary files /dev/null and b/sites/www/content/blog/the-power-of-webviews-in-flutter/images/1W_1b-8uIxDeFKplKkxtY0g.gif differ diff --git a/sites/www/content/blog/the-power-of-webviews-in-flutter/images/1XpAN7M4hnhe5OvJL3L22gw.gif b/sites/www/content/blog/the-power-of-webviews-in-flutter/images/1XpAN7M4hnhe5OvJL3L22gw.gif new file mode 100644 index 0000000000..847bcce297 Binary files /dev/null and b/sites/www/content/blog/the-power-of-webviews-in-flutter/images/1XpAN7M4hnhe5OvJL3L22gw.gif differ diff --git a/sites/www/content/blog/the-power-of-webviews-in-flutter/images/1aMgCX1bmq6hRNgmsDDVOXQ.gif b/sites/www/content/blog/the-power-of-webviews-in-flutter/images/1aMgCX1bmq6hRNgmsDDVOXQ.gif new file mode 100644 index 0000000000..aab94d6831 Binary files /dev/null and b/sites/www/content/blog/the-power-of-webviews-in-flutter/images/1aMgCX1bmq6hRNgmsDDVOXQ.gif differ diff --git a/sites/www/content/blog/the-power-of-webviews-in-flutter/images/1fdVsrNAivMi-VLPXWMW8gg.gif b/sites/www/content/blog/the-power-of-webviews-in-flutter/images/1fdVsrNAivMi-VLPXWMW8gg.gif new file mode 100644 index 0000000000..ee92fa19ad Binary files /dev/null and b/sites/www/content/blog/the-power-of-webviews-in-flutter/images/1fdVsrNAivMi-VLPXWMW8gg.gif differ diff --git a/sites/www/content/blog/the-power-of-webviews-in-flutter/images/1jqWObs72aipeAj8-1PmlZw.gif b/sites/www/content/blog/the-power-of-webviews-in-flutter/images/1jqWObs72aipeAj8-1PmlZw.gif new file mode 100644 index 0000000000..16612148c1 Binary files /dev/null and b/sites/www/content/blog/the-power-of-webviews-in-flutter/images/1jqWObs72aipeAj8-1PmlZw.gif differ diff --git a/sites/www/content/blog/the-power-of-webviews-in-flutter/images/1t_QQZf2S8t-i1IIDs4_Xcw.gif b/sites/www/content/blog/the-power-of-webviews-in-flutter/images/1t_QQZf2S8t-i1IIDs4_Xcw.gif new file mode 100644 index 0000000000..23cbb2e7a4 Binary files /dev/null and b/sites/www/content/blog/the-power-of-webviews-in-flutter/images/1t_QQZf2S8t-i1IIDs4_Xcw.gif differ diff --git a/sites/www/content/blog/the-power-of-webviews-in-flutter/index.md b/sites/www/content/blog/the-power-of-webviews-in-flutter/index.md new file mode 100644 index 0000000000..2a2773e63d --- /dev/null +++ b/sites/www/content/blog/the-power-of-webviews-in-flutter/index.md @@ -0,0 +1,195 @@ +--- +title: "The Power of WebViews in Flutter" +description: "Do you want to write an app that can display a web page without having to open your mobile device’s browser? Or maybe you already have a…" +publishDate: 2019-01-31 +author: efortuna +image: images/1aMgCX1bmq6hRNgmsDDVOXQ.gif +category: announcements +layout: blog +--- + +Do you want to write an app that can display a web page without having to open your mobile device’s browser? Or maybe you already have a secure payment flow implemented on your website and don’t want to reimplement payment functionality in your mobile app — money can be tricky business and you don’t want to end up accidentally sending half of the payment to the Save the [Kraken](https://en.wikipedia.org/wiki/Kraken) Fund. Oh, that’s just me? Well anyway, the Flutter team has created a [really cool plugin](https://pub.dartlang.org/packages/webview_flutter) that allows you to incorporate WebViews into your Flutter app to make all of this functionality possible. + +I meant the functionality of showing websites in Flutter apps… not rehabilitating krakens. + +## Flutter WebViews are just like any other widget + +Incorporating the WebView plugin into your app is extremely simple. It’s just a widget like any other: `WebView(initialUrl: ‘https://flutter.io')`. You can also optionally enable or disable JavaScript in your WebView with the `javascriptMode` parameter. By default, JavaScript in your WebView is disabled, so to enable it you’d construct a WebView like so: + +``` +WebView( + initialUrl: 'https://flutter.io', + javascriptMode: JavascriptMode.unrestricted, +) +``` + + +Pretty much all the information you want to know about your WebView and also the ability to control your WebView happens through the…wait for it… WebViewController. This controller is returned in a callback when the WebView is fully built: + +``` +WebViewController _controller; +WebView( + initialUrl: 'https://flutter.io', + onWebViewCreated: (WebViewController webViewController) { + _controller = webViewController; + }, +); + +//...later on, probably in response to some event: +_controller.loadUrl('http://dartlang.org/'); +``` + + +The WebViewController is your ticket to modifying the WebView programmatically from Flutter or accessing properties like the current URL being displayed. To see how all this works in practice, I wrote a simple Wikipedia-browsing app that allows you to bookmark pages for later, so that the completionist in you will never forget about that last fascinating Wikipedia article the next time you find yourself falling down a [Wiki rabbit hole](https://en.wikipedia.org/wiki/Wiki_rabbit_hole). + +<DashImage figure src="images/1aMgCX1bmq6hRNgmsDDVOXQ.gif" alt="A Wikipedia-exploring app written in Flutter using WebViews. You can favorite pages and save them for later viewing" caption="A Wikipedia-exploring app written in Flutter using WebViews. You can favorite pages and save them for later viewing" /> + + +The complete code for this Wiki-rabbit-hole-browser can be found at this [GitHub repository](https://github.com/efortuna/wiki_browser). + +```dart +class WikipediaExplorer extends StatefulWidget { + @override + _WikipediaExplorerState createState() => _WikipediaExplorerState(); +} + +class _WikipediaExplorerState extends State<WikipediaExplorer> { + Completer<WebViewController> _controller = Completer<WebViewController>(); + final Set<String> _favorites = Set<String>(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Wikipedia Explorer'), + // This drop down menu demonstrates that Flutter widgets can be shown over the web view. + actions: <Widget>[ + NavigationControls(_controller.future), + Menu(_controller.future, () => _favorites), + ], + ), + body: WebView( + initialUrl: 'https://en.wikipedia.org/wiki/Kraken', + onWebViewCreated: (WebViewController webViewController) { + _controller.complete(webViewController); + }, + ), + floatingActionButton: _bookmarkButton(), + ); + } + + _bookmarkButton() { + return FutureBuilder<WebViewController>( + future: _controller.future, + builder: + (BuildContext context, AsyncSnapshot<WebViewController> controller) { + if (controller.hasData) { + return FloatingActionButton( + onPressed: () async { + var url = await controller.data.currentUrl(); + _favorites.add(url); + Scaffold.of(context).showSnackBar( + SnackBar(content: Text('Saved $url for later reading.')), + ); + }, + child: Icon(Icons.favorite), + ); + } + return Container(); + }, + ); + } +} +``` + +WebView is just like any other widget in Flutter and can be composed with other widgets layering on top of it. Note that the favorite button is just a regular `FloatingActionButton` that is hovering on top of the WebView, complete with the shadow effect you would expect with the button. Also, when the drop down menu from the app bar is open, it partially covers the WebView just like with any other widget underneath the menu. + +If you look at the code, you may notice I use the `Completer` class and `FutureBuilder` frequently in this sample. Declaring the `_controller` instance variable as a Completer is like setting a placeholder for the WebViewController. We can check if we have the working WebViewController by calling _controller.isCompleted (meaning, it is completed when we have the valid WebViewController) or [using a FutureBuilder with _controller.future](https://github.com/efortuna/wiki_browser/blob/master/lib/main.dart#L40). Using a FutureBuilder allows us to build new UI components like the FloatingActionButton for adding favorites only after we have a working WebViewController (otherwise the program would have no way of getting [`currentUrl`](https://pub.dartlang.org/documentation/webview_flutter/latest/webview_flutter/WebViewController/currentUrl.html) when saving a favorite page). + +Two other features about WebViews can be a little complex, so let’s take a closer look at them in the following two sections. + +## WebViews can capture specific gestures, too + +Since WebViews are Flutter widgets, they can participate in Flutter’s gesture disambiguation protocol ([aka the Gesture Arena](https://flutter.io/gestures/#gesture-disambiguation)). By default, a WebView only responds to a gesture if no other widget claimed it. But you can make the WebView proactively claim a gesture by specifying `gestureRecognizers`. + +If your WebView is inside another widget that responds to gestures, for example a ListView, you might want to specify how your app responds to gestures. If the user drags their finger across the screen, should you scroll the ListView or the WebView? If you want both widgets to be scrollable, the WebView widget can “capture” the drag gesture so that it scrolls when the user drags the WebView, but scrolls the ListView otherwise. You can specify which gestures get passed on to the WebView widget with the `gestureRecognizers` parameter. The parameter takes a Set of all the GestureRecognizers that you want to capture. Don’t be scared off by that Factory object, either — it’s basically just a glorified builder method. To capture the vertical scroll event, I can write this: + +``` +WebView( + initialUrl: someUrl, + gestureRecognizers: Set() + ..add(Factory<VerticalDragGestureRecognizer>( + () => VerticalDragGestureRecognizer())), +) +``` + + +Or, written another way: + +``` +var verticalGestures = Factory<VerticalDragGestureRecognizer>( + () => VerticalDragGestureRecognizer()); +var gestureSet = Set.from([verticalGestures]); +return WebView( + initialUrl: someUrl, + gestureRecognizers: gestureSet, +); +``` + + +If you watch the [Boring Flutter Development Show](https://www.youtube.com/playlist?list=PLOU2XLYxmsIK0r_D-zWcmJ1plIcDNnRkK) at all, you may have seen us develop the Kraken News, I mean the *Hacker* News Reader App. + +<DashImage figure src="images/1jqWObs72aipeAj8-1PmlZw.gif" alt="latest version of the Hacker News app that we’ve been developing on our YouTube show" caption="latest version of the Hacker News app that we’ve been developing on our YouTube show" /> + + +To show gesture capturing in the context of an app, I modified the Hacker News app to show part of the webpage as a “preview.” This allows the user to scroll through the linked page vertically to determine if they want to open it in a separate page for more in-depth reading. + +<DashImage figure src="images/1XpAN7M4hnhe5OvJL3L22gw.gif" alt="Hacker News Reader app with WebView previews of the links. The WebViews capture the vertical drag gesture to enable scrolling of the previews." caption="Hacker News Reader app with WebView previews of the links. The WebViews capture the vertical drag gesture to enable scrolling of the previews." /> + + +The code for this Hacker News reader app can be found at [this GitHub repo](https://github.com/efortuna/hn_app/blob/master/lib/main.dart). (perhaps show code for `_buildItem` here). + +However, if the WebView is inside a widget that only captures gestures you don’t care about, no gesture detector is needed. For example, a PageController only responds to horizontal drag gestures, and you just want the WebView to be able to scroll vertically, you can write the code like this: + +``` +PageView(children: [ + WebView(initialUrl: urlOne), + WebView(initialUrl: urlTwo), + WebView(initialUrl: urlThree), +])); +``` + + +<DashImage figure src="images/1t_QQZf2S8t-i1IIDs4_Xcw.gif" alt="WebViews in a PageView. The PageView controls the horizontal swiping and the WebViews can scroll vertically with no extra work on your part." caption="WebViews in a PageView. The PageView controls the horizontal swiping and the WebViews can scroll vertically with no extra work on your part." /> + + +## Your WebViews might need a key parameter + +You’ve probably seen those ubiquitous optional key parameters sprinkled on just about every Widget constructor in the Flutter codebase. Keys are needed if you have multiple stateful widgets of the same type that are removed, added, or reordered in your app. As it turns out, WebView is a stateful widget (the state including the current page and browser history). Therefore, if you have multiple WebViews in your app, you may need to add a key parameter. + +An example of this situation is actually in the Hacker News app! Here’s what happens if we switch tabs and our WebViews don’t have keys: + +<DashImage figure src="images/1W_1b-8uIxDeFKplKkxtY0g.gif" alt="This is what happens if you don’t use keys in a stateful app. When we change to the “New Stories” tab the wrong web preview page displays." caption="This is what happens if you don’t use keys in a stateful app. When we change to the “New Stories” tab the wrong web preview page displays." /> + + +As you can see, when we switch tabs, the “Interview with Alan Kay” tile is expanded, but the webview still shows the BBC page about Virgin Galactic! This is fixed by putting a key at the *topmost* collection widget (in this case the `ExpansionTile`): + +<DashImage figure src="images/1fdVsrNAivMi-VLPXWMW8gg.gif" alt="Whew! Using keys fixes the problem by showing a different WebView (and the corresponding correct website) for each item in the reader app" caption="Whew! Using keys fixes the problem by showing a different WebView (and the corresponding correct website) for each item in the reader app" /> + + +The super abbreviated explanation of why keys solve this is that when Flutter switches the list of stories to display, it sees that each set of stories is made of a ListView with ExpansionTile items. Flutter has a fast comparison algorithm to avoid unnecessarily redrawing the screen that checks the widget type and key. Without a key, because the widget types of each list are the same, the stateless items (like link titles) all get updated, but stateful components, (like the expanded state of ExpansionTile and the website’s URL), they fail to get redrawn. Adding a key fixes this issue. For a more thorough explanation, check out this video on keys: + +<YoutubeEmbed id="kn0EOS-ZiIc" title="When to Use Keys - Flutter Widgets 101 Ep. 4" fullwidth="true"/> + + +Similarly, if you use WebViews in the context of a [Hero](https://flutter.io/docs/development/ui/animations/hero-animations) widget, you’ll want to use a global key so that Flutter knows the two WebViews are actually the same and doesn’t try to re-render the second. + +## A few remaining things to bear in mind: + +The WebView plugin is currently in Developer Preview while we finish adding polish. This means that if you want to run the webview plugin on iOS, you also need to add the following line inside the `<dict>` in your `ios/Runner/Info.plist`: + +`<key>io.flutter.embedded_views_preview</key><string>yes</string>` [as described in this GitHub issue](https://github.com/flutter/flutter/issues/19030#issuecomment-437534853). + +There is another community-based WebView plugin though it doesn’t have all of the functionality of the above plugin by the Flutter team. It is simply displays a webpage in a native view, and is not integrated with the rest of the Flutter widget tree. Therefore that version does not allow you to compose WebViews with arbitrary other Flutter widgets. Using the [webview_flutter plugin](https://pub.dartlang.org/packages/webview_flutter) described in this article avoids this problem. + +That’s all folks! Go forth and add WebViews to your Flutter apps. And give those krakens some love too. \ No newline at end of file diff --git a/sites/www/content/blog/the-top-ten-highlights-from-flutter-dart-in-2025/images/138nWNBqHADyCszyyX-h98Q.webp b/sites/www/content/blog/the-top-ten-highlights-from-flutter-dart-in-2025/images/138nWNBqHADyCszyyX-h98Q.webp new file mode 100644 index 0000000000..d42628c6f5 Binary files /dev/null and b/sites/www/content/blog/the-top-ten-highlights-from-flutter-dart-in-2025/images/138nWNBqHADyCszyyX-h98Q.webp differ diff --git a/sites/www/content/blog/the-top-ten-highlights-from-flutter-dart-in-2025/index.md b/sites/www/content/blog/the-top-ten-highlights-from-flutter-dart-in-2025/index.md new file mode 100644 index 0000000000..8ff04074b3 --- /dev/null +++ b/sites/www/content/blog/the-top-ten-highlights-from-flutter-dart-in-2025/index.md @@ -0,0 +1,159 @@ +--- +title: "The Top Ten Highlights from Flutter & Dart in 2025" +description: "Hi, Flutter community! My name is Emma Twersky, and I’m back with monthly updates from the Flutterverse. I’m so thankful for the warm…" +publishDate: 2025-12-17 +author: twerske +image: images/138nWNBqHADyCszyyX-h98Q.webp +layout: blog +--- + +<DashImage figure src="images/138nWNBqHADyCszyyX-h98Q.webp" /> + + +## The Top Ten Highlights from Flutter & Dart in 2025 + +Hi, Flutter community! My name is Emma Twersky, and I’m back with monthly updates from the Flutterverse. I’m so thankful for the warm welcome, and excited to be a part of this community. + +This post is a bit of a two-for-one: we’re diving into hot new content from today’s livestream, “[Flutter Flight Plans](https://www.youtube.com/watch?v=pGN8kq7QDgg),” helping you take flight on some of our most requested topics. Then we’ll take a moment to reflect on what a truly landmark year 2025 has been. We’re all-in on making Dart & Flutter the best way to build amazing multi-platform apps, and we’re excited to show you what’s new and what’s next. + +## 10 Highlights from Flutter & Dart in 2025 + +What a year! Let’s dig into just 10 of the top moments, although it was hard narrowing it down… + +As you might know, Dart and Flutter ship quarterly updates. In 2025, we shipped eight packed stable releases: + +1. **[Flutter 3.29](https://blog.flutter.dev/whats-new-in-flutter-3-29-f90c380c2317) & [Dart 3.7](https://blog.dart.dev/announcing-dart-3-7-bf864a1b195c) (Feb 2025):** This release was all about performance and fidelity, with big Impeller updates, the move of Dart execution to the main thread on mobile to improve native interop, and a new Dart formatter. + +2. **[Flutter 3.32](https://blog.flutter.dev/whats-new-in-flutter-3-32-40c1086bab6e) & [Dart 3.8](https://blog.dart.dev/announcing-dart-3-8-724eaaec9f47) (May 2025[)](https://blog.flutter.dev/whats-new-in-flutter-3-32-40c1086bab6e):** We got experimental web hot reload, Cupertino “Squircles,” stylus support on Android, cross-compilation for Dart, and null-aware elements. + +3. **[Flutter 3.35](https://blog.flutter.dev/whats-new-in-flutter-3-35-c58ef72e3766) & [Dart 3.9](https://blog.dart.dev/announcing-dart-3-9-ba49e8f38298) (Aug 2025):** Web hot reload became default, we launched experimental Widget Previews, introduced the Dart & Flutter [MCP server](https://docs.flutter.dev/ai/mcp-server), and announced the Material/Cupertino decoupling. + +4. **[Flutter 3.38](https://medium.com/r?url=https%3A%2F%2Fblog.flutter.dev%2Fwhats-new-in-flutter-3-38-3f7b258f7228) & [Dart 3.10](https://blog.dart.dev/announcing-dart-3-10-ea8b952b6088) (Nov 2025):** Dart added dot shorthands, web tooling improved, and Widget Previews got IDE integration! Plus, crucial iOS 26/Xcode 26 support and the UIScene migration path. + +5. We laid out our vision early in the year, sharing our [**Flutter 2025 Roadmap Update**](https://blog.flutter.dev/flutter-2025-roadmap-f127882b117f) and it’s exciting to see how much has been delivered and the continued focus on multi-platform excellence and AI integration. + +… and since many of you have been asking, yes, we plan to be back in early 2026 with a similar annual post as we continue to adjust our roadmap based on your feedback. Our goal is ultimately to continue building the best cross-platform framework with the best developer experience and feature set. + +6. Speaking of you… **the Flutter community and ecosystem are thriving!** At [Google I/O](https://blog.flutter.dev/dart-flutter-momentum-at-google-i-o-2025-4863aa4f84a4), we shared that **nearly 30% of new free iOS apps are built with Flutter**. + +We’re constantly amazed by what you’re building. In 2025, we worked with some amazing companies to showcase how they’re building and betting on Flutter and Dart in big ways, including NotebookLM, teamLab, Agape, Universal Destinations & Experiences, Reflection.app, and GE Appliances. + +Some highlights from this year include: + +**talabat** is MENA’s leading on demand delivery platform. With millions of daily users, talabat uses Flutter to connects millions of customers to food, groceries, and more across 8 countries. + +<YoutubeEmbed id="A3ApoV_QRio" title="Rewriting an app with millions of users: The Talabat Story" fullwidth="true"/> + + +**Google’s NotebookLM** used Flutter to meet explosive user demand, delivering a delightful 4.8-star mobile experience in under 7 months. + +<YoutubeEmbed id="pMoUg3dkDJk" title="How NotebookLM built and shipped a 4.8-Star Flutter App in 7 Months" fullwidth="true"/> + + +**teamLab** uses Flutter in art and digital solutions, extending experiences to over 35 million people. + +<YoutubeEmbed id="OyS812ZoTzw" title="teamLab: Flutter developer story" fullwidth="true"/> + + +Flutter is woven into **Agapé Wellness’s** mission to help people feel close, even when apart. + +<YoutubeEmbed id="H9njcR0fjO0" title="Agapé: Flutter developer story" fullwidth="true"/> + + +7. As we continue growing, it’s important to understand **How Flutter Works.** So, we made [a whole series on that](https://www.youtube.com/watch?v=0Xn1QhNtPkQ&list=PLjxrf2q8roU1nbstACpBBSwHa-BuOILlM). + +<YoutubeEmbed id="0Xn1QhNtPkQ" title="How Flutter Works: Architecture #DecodingFlutter (1/6)" fullwidth="true"/> + + +Now that we know how it works, let’s make it more flexible for the future. + +8. The work to **decouple design systems** and move Material and Cupertino into their own packages is underway, as announced in [Flutter 3.35](https://blog.flutter.dev/whats-new-in-flutter-3-35-c58ef72e3766). This is a big step for the maturity and flexibility of the ecosystem. I’ll let Justin explain exactly what this means: + +<YoutubeEmbed id="W4olXg91iX8" title="Strengthening Flutter's core widgets" fullwidth="true"/> + + +9. We know speed matters… so **we made build_runner faster** (two or more times in some cases!). John shares how we’re accelerating Dart code generation in this great video: + +<YoutubeEmbed id="6PKIv7yUKwQ" title="Accelerating Dart code generation" fullwidth="true"/> + + +10. Another thing that sped up in 2025 is the pace of AI. + +**AI truly took center stage in the Flutter and Dart ecosystem.** The [Building Agentic Apps](https://flutter.dev/events/building-agentic-apps#home) series and our announcements at [Google I/O](https://blog.flutter.dev/dart-flutter-momentum-at-google-i-o-2025-4863aa4f84a4) really set the tone, and we’re ending the year with two [Flutter Flight Plans](https://www.youtube.com/live/zNHoHAPizHM?si=ysUxB6Q2FnSOGmXb) to prepare for take off into the future of apps. 2025 brought a massive leap in AI-assisted development. Read all about it in our [Create with AI](https://docs.flutter.dev/ai/create-with-ai) section of the Flutter documentation. + +The [**Dart and Flutter MCP Server**](https://blog.flutter.dev/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server-2edcc8107b49) is now stable, empowering tools like Gemini CLI, Antigravity, and wherever you’re building with AI. +It’s been incredible to see [Gemini in Android Studio supercharge Dart and Flutter development](https://blog.flutter.dev/gemini-in-android-studio-now-speaks-fluent-flutter-915dfec98274). And the [Flutter Extension for Gemini CLI](https://blog.flutter.dev/meet-the-flutter-extension-for-gemini-cli-f8be3643eaad)? Game changer for AI-assisted app building. + +We’re also announcing the [Flutter AI Toolkit](https://docs.flutter.dev/ai-toolkit) v1.0, now ready for your production workloads. The Flutter AI Toolkit is a set of AI-related widgets to make it easy to add an GenAI features to your Flutter app. Since our last beta release, the AI Toolkit has migrated to the Firebase AI Logic SDK, adding new features along the way, including multi-turn function calling for agentic workflows, the ability to plug-in custom speech-to-text translators, and full support for turning LLM responses into widgets. + +And speaking of new tools, Google introduced [**Antigravity**](https://docs.flutter.dev/ai/create-with-ai#antigravity), a new IDE experience aimed at boosting developer workflows with AI. We’re partnering with teams across Google to make Flutter development a world-class experience in all of the ways that Google AI is available. Watch [the Observable<Flutter> livestream](https://www.youtube.com/live/s5uGpolyHd4?si=7Jrct-RHW2lTiKq0) for more. + +I want to share one more thing that we’re most excited about heading into 2026… + +11. We launched the alpha of the [**GenUI SDK for Flutter**](https://blog.flutter.dev/rich-and-dynamic-user-interfaces-with-flutter-and-generative-ui-178405af2455) on pub.dev, opening doors for creating dynamic UIs with LLMs. + +GenUI is here to help you generate Flutter UI from natural language or structured data, potentially speeding up your development flow significantly. I’ll let Khanh dive into the details: + +<YoutubeEmbed id="K2p5Nrn2OSU" title="Building interactive UIs for Agent-powered apps with Generative UI" fullwidth="true"/> + + +It’s fantastic to see the community already building on GenUI! +Check out two community members doing just that: + +* [Khalid built an AI Concierge Agent using GenUI](https://x.com/KhalidWar/status/1995304031129071718), including a great recap video. Definitely check it out! + +* **dartantic by Chris Sells** +The [dartantic_ai package](https://pub.dev/packages/dartantic_ai) is an agent framework designed to build multi-model agentic apps that run wherever Dart runs, including mobile, web, desktop, the CLI, or on the server. With the new 2.0 release, [dartantic provides a ContentGenerator](https://github.com/flutter/genui/pull/583/) for implementation in the new Flutter GenUI project. This enables you to plug in any provider or model as well as taking advantage of advanced features like server-side tools — like web search and code execution — and media generation, including out of the box support for Google’s new Nano Banana Pro. + +**We’re working to make our GenUI SDK an industry leader.** + +One way is partnering with the teams behind [**ADK**](https://google.github.io/adk-docs/) and [**A2UI**](https://a2ui.org/), offering a more template-driven approach to UI generation. Rody’s talk explores how Flutter is leveraging ADK: + +<YoutubeEmbed id="Dum-LYLm0Uw" title="How to connect interfaces to the cloud with AI agents" fullwidth="true"/> + + +## What are you building over winter break? + +I ended that list feeling inspired and *overwhelmed*. There’s so much innovation it’s sometimes hard to keep up. But luckily, the holidays are a perfect time to tinker! + +We’re all excited for the holidays for different reasons, some are headed skiing, spending time with loved ones, but it’s also a chance to dig in and learn something new. + +If that’s you, we’d love to see what you build. Here are some ideas we’ve brainstormed: + +1. **Rebuild your personal site in Jaspr — and use Antigravity to make it easier to change up your tech stack** +Take an existing website and use an agent to migrate it to Jaspr in a fraction of the time it would take manually, we’ve found Antigravity is *very* good at this. + +<YoutubeEmbed id="w05Mwgk_R4g" title="Building websites with Dart and Jaspr" fullwidth="true"/> + + +2. **Start learning about the future of Dynamic UI with GenUI** +Build a GenUI Live Sketch Pad app where typed UI descriptions are rendered in real-time by GenUI. Or, if you build something else with the SDK, let us know on socials. The package is alpha so we’re actively looking for feedback on your experience using it! + +3. **Build an Agentic Workflow:** Install the [Dart MCP server](https://docs.flutter.dev/ai/mcp-server) and create a workflow that takes a screenshot of your app, gets a critique from Gemini, and applies the changes with hot reload. + +4. **Generate UIs from Thin Air:** Take your workflow one step further by using [Stitch](https://stitch.withgoogle.com/) to design a UI, then ask Gemini to write the Flutter code for you. + +5. **Become a Flutter contributor:** Make your first contribution to Flutter. Find and fix an open documentation issue or tackle a “good first bug.” + +<YoutubeEmbed id="4yBgOBAOx_A" title="How to contribute to Flutter (The Boring Flutter Development Show, Ep. 53)" fullwidth="true"/> + + +6. **Crush Performance Bugs:** Use DevTools to [profile your app’s performance](https://docs.flutter.dev/perf). Identify and fix a performance bottleneck using tracing and your coding agent. + +7. **Modernize Your Codebase:** Supercharge your existing Flutter project by refactoring it with [Dart 3.10’s dot shorthands](https://dart.dev/language/dot-shorthands) for cleaner, more concise code. + +8. **Exploring Gemini APIs in Flutter** Build a voice app showcasing different Gemini API features using the [Flutter AI Playground](http://g.co/firebase/flutter-ai-playground) sample app which uses the Firebase AI Logic SDK. + +9. **Build Your Own Code Assistant:** Train an agent on your GitHub repository to automatically manage your package dependencies or perform other tedious tasks. + +10. **Level Up Your App with the Ecosystem:** Integrate a service from a Flutter-based business like [Shorebird](https://shorebird.dev/) for over-the-air updates or [Serverpod](https://serverpod.dev/) for a scalable backend. + +Share your winter break projects with the community using the hashtag #Flutter2026. + +## Thank you for being a part of Flutter in 2025, see you in 2026! + +With the foundations laid in 2025, we’re incredibly excited for 2026 as we push the boundaries of multi-platform development, dive deeper into AI-driven ephemeral experiences, and empower you to build the truly full-stack, cross-platform applications of the future. + +A huge THANK YOU to the incredible Flutter and Dart communities. Your passion, feedback, and contributions made 2025 amazing. We feel so lucky to be a part of this community. 💙 + +Wishing you all a wonderful and innovative winter break. We can’t wait to see what you back and building in 2026! \ No newline at end of file diff --git a/sites/www/content/blog/time-is-ticking-clock-contest-live-with-over-10-000-in-prizes/images/1uDHQJzyDKsFEz4X59pi5Uw.webp b/sites/www/content/blog/time-is-ticking-clock-contest-live-with-over-10-000-in-prizes/images/1uDHQJzyDKsFEz4X59pi5Uw.webp new file mode 100644 index 0000000000..e7de950bea Binary files /dev/null and b/sites/www/content/blog/time-is-ticking-clock-contest-live-with-over-10-000-in-prizes/images/1uDHQJzyDKsFEz4X59pi5Uw.webp differ diff --git a/sites/www/content/blog/time-is-ticking-clock-contest-live-with-over-10-000-in-prizes/index.md b/sites/www/content/blog/time-is-ticking-clock-contest-live-with-over-10-000-in-prizes/index.md new file mode 100644 index 0000000000..bad23ea9c0 --- /dev/null +++ b/sites/www/content/blog/time-is-ticking-clock-contest-live-with-over-10-000-in-prizes/index.md @@ -0,0 +1,30 @@ +--- +title: "Time is Ticking: Clock Contest live with over $10,000 in prizes" +description: "Flutter Clock is a contest offered by Google, with participation from the Flutter, Google Assistant, and Lenovo teams, that challenges you…" +publishDate: 2019-11-18 +author: aguinis +image: images/1uDHQJzyDKsFEz4X59pi5Uw.webp +category: events +layout: blog +--- + +[Flutter Clock](http://flutter.dev/clock) is a contest offered by Google, with participation from the Flutter, Google Assistant, and Lenovo teams, that challenges you to build a Flutter clock face application for the Lenovo Smart Clock that is beautiful and innovative. Whether you’re a Flutter expert or novice, we invite you to join us and see what you can create. Over $10,000 in prizes will be awarded to the winners! Visit [flutter.dev/clock](http://flutter.dev/clock) to enter. + +<YoutubeEmbed id="rxs69_szCkE" title="FOO014 Final" fullwidth="true"/> + + +**High Level Details** + +***Date:** All entries must be submitted by January 20, 2020 11:59 PM PST (GMT-8).* + +***How to Submit:** Entries will be collected on the form linked at [flutter.dev/clock](http://flutter.dev/clock), but see the [Official Rules](https://docs.google.com/document/d/1ybyQCK8Sy7vrD9wuc6pbgwVkyrVZ7Rd_41r5NXGqlt8/edit) for full details.* + +***Winners:** Submissions will be rated by Google and Flutter expert judges against the following rubric: visual beauty, code quality, novelty of idea, and overall execution.* + +***Prizes**: Potential prizes include a fully loaded iMac Pro, Lenovo Smart Display, and Lenovo Smart Clock. Also, all complete and valid submissions will receive a digital certificate of completion. In addition, some of the clock contest submissions might be integrated into the Lenovo Smart Clock’s lineup of clock faces, or used as inspiration for future clock faces!* + +*Results will be announced at our Mobile World Congress 2020 Keynote.* + +Good luck and have fun! Time is ticking… + +<DashImage figure src="images/1uDHQJzyDKsFEz4X59pi5Uw.webp" /> diff --git a/sites/www/content/blog/tune-in-for-flutterinproduction-coming-soon-to-a-screen-near-you/images/1SSIgDyW3jMaOF5nxH5D9lw.gif b/sites/www/content/blog/tune-in-for-flutterinproduction-coming-soon-to-a-screen-near-you/images/1SSIgDyW3jMaOF5nxH5D9lw.gif new file mode 100644 index 0000000000..b0a6f2dc71 Binary files /dev/null and b/sites/www/content/blog/tune-in-for-flutterinproduction-coming-soon-to-a-screen-near-you/images/1SSIgDyW3jMaOF5nxH5D9lw.gif differ diff --git a/sites/www/content/blog/tune-in-for-flutterinproduction-coming-soon-to-a-screen-near-you/index.md b/sites/www/content/blog/tune-in-for-flutterinproduction-coming-soon-to-a-screen-near-you/index.md new file mode 100644 index 0000000000..2af7d844e1 --- /dev/null +++ b/sites/www/content/blog/tune-in-for-flutterinproduction-coming-soon-to-a-screen-near-you/index.md @@ -0,0 +1,36 @@ +--- +title: "Tune in for #FlutterInProduction — coming soon to a screen near you!" +description: "Mark your calendars and be sure to tune into #FlutterInProduction at 11:00am PT on December 17, 2024." +publishDate: 2024-12-03 +author: kkboateng +image: images/1SSIgDyW3jMaOF5nxH5D9lw.gif +category: events +layout: blog +--- + +## Tune in for #FlutterInProduction — coming soon to a screen near you! + +Mark your calendars and be sure to tune into **#FlutterInProduction** on **December 17, 2024** at 11:00am PT / 2:00pm ET / 20:00 CET. + +<DashImage figure src="images/1SSIgDyW3jMaOF5nxH5D9lw.gif" /> + + +This virtual event, hosted on the [Flutter YouTube channel](https://www.youtube.com/@flutterdev), is a little different than our previous events. We’re not just diving into the latest releases (though we’ll definitely be doing that!): #FlutterInProduction is about celebrating the incredible journey we’ve taken together as a community to make Flutter the powerful production-ready framework it is today. + +## What to expect + +* **#FlutterInProduction:** Whether you’re a seasoned pro or just starting out, you’ll gain new insights and inspiration from Flutter user stories, learn more about the latest features from Flutter technical leads, and see what the future holds for Flutter app development in this multi-segment virtual event. + +* **#AskFlutter Live Q&A:** Get your questions answered by Flutter team leads. Stay tuned for our call for questions on social media! + +* **Observable Flutter**: Have any questions about what it’s like to build with Flutter in an enterprise development environment? Get those questions answered by tech leads at companies using Flutter in production. + +## Stay tuned + +We’ve got a couple surprises and announcements to make along the way, so be sure to take the following steps to stay in-the-know. + +1. [Set a reminder](https://calndr.link/event/jqZQr0d7nH) for the event time on your calendar, or on [YouTube](https://www.youtube.com/@flutterdev). + +1. Follow us on [Twitter](https://www.youtube.com/redirect?event=channel_description&redir_token=QUFFLUhqbkRYNDVxWGRFUUZYNVdmMlg4QTB4akhOeXNVd3xBQ3Jtc0trWWpSSHo0Tnh3QjlKdF81Z0lsSkQzQWdxVEpBNVdOSVE5Q01tUUltMUFvV2k5Z1RfUzVtc0JET0pFejVRLTl1UUdPNG9DMHk3ZWpUcGtRVGVtUk1yUzA3SEx4STVpM09Kd0dSczVxOGhyQkxRb1EwNA&q=https%3A%2F%2Ftwitter.com%2FFlutterDev), [LinkedIn](https://www.linkedin.com/showcase/flutterdevofficial/), or here on Medium! + +We can’t wait to share #**FlutterinProduction** with you! \ No newline at end of file diff --git a/sites/www/content/blog/two-months-of-fluttergoodnewswednesday/images/0B33kcAC_6aXEEdVZ.webp b/sites/www/content/blog/two-months-of-fluttergoodnewswednesday/images/0B33kcAC_6aXEEdVZ.webp new file mode 100644 index 0000000000..0a4bdf4c41 Binary files /dev/null and b/sites/www/content/blog/two-months-of-fluttergoodnewswednesday/images/0B33kcAC_6aXEEdVZ.webp differ diff --git a/sites/www/content/blog/two-months-of-fluttergoodnewswednesday/images/0h3Nhe6QdketsMi-z.webp b/sites/www/content/blog/two-months-of-fluttergoodnewswednesday/images/0h3Nhe6QdketsMi-z.webp new file mode 100644 index 0000000000..2a23e1d9ec Binary files /dev/null and b/sites/www/content/blog/two-months-of-fluttergoodnewswednesday/images/0h3Nhe6QdketsMi-z.webp differ diff --git a/sites/www/content/blog/two-months-of-fluttergoodnewswednesday/images/1VB60_VnsDmTKBsewFgus_Q.webp b/sites/www/content/blog/two-months-of-fluttergoodnewswednesday/images/1VB60_VnsDmTKBsewFgus_Q.webp new file mode 100644 index 0000000000..3e6d3389d4 Binary files /dev/null and b/sites/www/content/blog/two-months-of-fluttergoodnewswednesday/images/1VB60_VnsDmTKBsewFgus_Q.webp differ diff --git a/sites/www/content/blog/two-months-of-fluttergoodnewswednesday/index.md b/sites/www/content/blog/two-months-of-fluttergoodnewswednesday/index.md new file mode 100644 index 0000000000..76330a8f10 --- /dev/null +++ b/sites/www/content/blog/two-months-of-fluttergoodnewswednesday/index.md @@ -0,0 +1,123 @@ +--- +title: "Two Months of #FlutterGoodNewsWednesday" +description: "New apps and plugins, free training and tooling: oh my!" +publishDate: 2020-05-27 +author: timsneath +image: images/0B33kcAC_6aXEEdVZ.webp +layout: blog +--- + +When we realized that this was going to be a very different spring without the usual events and connection points, we decided to find a new way to share our various announcements with you. A few of us put our heads together, and [#FlutterGoodNewsWednesday](https://twitter.com/hashtag/fluttergoodnewswednesday) was born. Each week for the last two months, we’ve shared something new that we hope made you smile. This week, we thought we’d follow up on some of the announcements we’ve made so far and share some further updates. + +## Free introductory training for Flutter developers + +We kicked things off with an announcement of a [free 10+ hour introductory course covering the basics of Flutter development](https://medium.com/flutter/learn-flutter-for-free-c9bc3b898c4d), created in partnership with Angela Yu of the App Brewery. We’re delighted to see that **over 75,000** of you have signed up for the course, with nearly 5,000 completions already (my thirteen year-old son is one of them!) For those of you on the course, hopefully you’ve discovered the Discord group where [Very Good Ventures](https://verygood.ventures/) and a team of volunteers have been providing concierge support. We’ve been learning from you too — in particular, understanding where some of the early pitfalls are to getting started; we’ll use this knowledge to improve the documentation and the product itself. + +If you’ve almost completed this course, you might be wondering “what next”? We have a few recommendations for you that cover intermediate topics: + +* [Part 2 of Angela’s course](https://www.udemy.com/course/flutter-bootcamp-with-dart/?referralCode=2B7724A180C0502A2547) covers calling web services and Firebase integration, more advanced widgets, state management, and animation. + +* [Nick Manning’s Tourism & Co course](https://fluttercrashcourse.com/courses/tourismco) puts what you learn in Angela’s course into action with a real, working app example, including unit and integration testing, asynchronous web service invocation, JSON APIs and custom views. + +* Similarly, [Andrea Bizotto’s REST API crash course](https://courses.codewithandrea.com/) builds on a popular API, showing how to call REST-based web services, cache returned data, and handle service degradation issues. + +## CodePen Flutter support + +We also shared about [CodePen’s adoption of Flutter](https://medium.com/flutter/announcing-codepen-support-for-flutter-bb346406fe50). To date we’ve seen nearly 5,000 Flutter pens created by designers and developers from around the world. Some of you have boggled our minds with what you’ve created. Here are a few favorites that crossed our radar in the last weeks: + +* [Retro Boy Color](https://codepen.io/mkiisoft/full/bGVxrWK) (by [Mariano Zorrilla](https://codepen.io/mkiisoft)) + +* [Netflix Logo in Flutter](https://codepen.io/orestesgaolin/full/xxwzdgJ) (by [Dominik Roszkowski](https://codepen.io/orestesgaolin)) + +* [Flutter Space Program](https://codepen.io/orestesgaolin/full/qBOxpBK) (by [Dominik Roszkowski](https://codepen.io/orestesgaolin)) + +* [Draggable C curve](https://codepen.io/Darth_Paul/full/YzyrmGy) (by [Paul Thomas](https://codepen.io/Darth_Paul)) + +* [Liquid Polygon](https://codepen.io/blueaquilae/full/pojWyZg) (by [Robert Felker](https://codepen.io/blueaquilae)) + +* [Long shadow animations](https://codepen.io/jonathan_monga/full/wvKZbzG) (by [Jonathan Monga](https://codepen.io/jonathan_monga)) + +* [Trigonometric Flutter](https://codepen.io/Deven-Joshi/full/NWGdvgG) (by [Deven Joshi](https://codepen.io/Deven-Joshi)) + +* [Color Test](https://codepen.io/chiziaruhoma-ogbonda/full/oNjObRm) (by [Chiziaruhoma Ogbonda](https://codepen.io/chiziaruhoma-ogbonda/)) + +<DashImage figure src="images/0B33kcAC_6aXEEdVZ.webp" alt="New CodePens that use Flutter’s web support to showcase prototypes and experiments." caption="New CodePens that use Flutter’s web support to showcase prototypes and experiments." /> + + +Keep exploring with CodePen: we have a challenge coming up for you! + +## Flutter Momentum + +For our third #FlutterGoodNewsWednesday blog post, [we shared some statistics about Flutter adoption, with two million developers now using Flutter](https://medium.com/flutter/flutter-spring-2020-update-f723d898d7af). There are now more developers using Flutter on a typical day than over the course of an entire week this time last year. + +<DashImage figure src="images/0h3Nhe6QdketsMi-z.webp" alt="The New York Times Spelling Bee game, implemented with Flutter." caption="The New York Times Spelling Bee game, implemented with Flutter." /> + + +At the time we shared that we’d seen about 50,000 apps uploaded to the Play Store. The pace of growth continues to accelerate, and we’re delighted to share that **a further 10,000 Flutter apps have been uploaded in the month since that post**. This level of momentum inspires us: thank you for your support! + +A great example of one of those new apps is from the [New York Times games team](https://www.nytimes.com/subscription/games/lp8J6CG), who shared some of their experiences with Flutter [at the Google I/O ’19 event](https://developers.googleblog.com/2019/05/Flutter-io19.html). Their [Spelling Bee](https://www.nytimes.com/puzzles/spelling-bee) game is now rolling out to the iOS and Android stores as part of their [crossword app](https://play.google.com/store/apps/details?id=com.nytimes.crossword&hl=en_US): built entirely with Flutter, this addictive word game challenges you to make words out of a honeycomb of letters. + +Since this blog post came out, we’ve [also released the results of our Q1 developer survey](https://medium.com/flutter/what-are-the-important-difficult-tasks-for-flutter-devs-q1-2020-survey-results-a5ef2305429b), which over 6,000 of you responded to. 94.5% of you say that you’re satisfied or very satisfied with Flutter, but we’re also learning more about areas of difficulty. One challenge reported in a previous survey was animation, and we’ve subsequently added a [series of videos](https://www.youtube.com/playlist?list=PLjxrf2q8roU2v6UqYlt_KPaXlnjbYySua) and [more detailed documentation](https://flutter.dev/docs/development/ui/animations) targeting that subsystem in greater detail. + +## Web Progress Update + +One area we’ve been working hard on over the last few months has been Flutter support for targeting web output. [As we reported in our web update](https://medium.com/flutter/flutter-web-support-updates-8b14bfe6a908), we are particularly focused on improving both performance and conformance. In the post we announced that Flutter now supports Progressive Web Applications, and in the intervening weeks we’ve been refining this feature substantially with [more sophisticated service worker caching](https://github.com/flutter/flutter/pull/56103) to improve first-load performance. + +In the last week, we’ve also begun a [series of articles](https://medium.com/flutter/optimizing-performance-in-flutter-web-apps-with-tree-shaking-and-deferred-loading-535fbe3cd674) talking about [tips and tricks for maximizing performance of Flutter apps that run on the web](https://medium.com/flutter/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation-6b3601087a2b), based on learnings from optimizing the Flutter Gallery. Keep following this blog channel for more articles on this subject in the coming weeks! + +## Flutter 1.17 and Dart 2.8 + +Perhaps the biggest news so far with this slew of announcements was the [release of Flutter 1.17](https://medium.com/flutter/announcing-flutter-1-17-4182d8af7f8e), including [Dart 2.8](https://medium.com/dartlang/announcing-dart-2-8-7750918db0a). Flutter 1.17 offers performance and memory improvements on all platforms, but you’ll see the biggest boosts on iOS, with the [adoption of Apple’s Metal subsystem](https://medium.com/flutter/announcing-flutter-1-17-4182d8af7f8e#f303) for graphics rendering. For Android too, you’ll see improvements, particularly with the new optional [fast start debugging mode](https://github.com/flutter/flutter/pull/46140), which offers a 3x speed boost with no changes to your code. + +Beyond the new features mentioned in the blog post, one feature that some of you have discovered is the updated error display on release mode builds. For release mode builds only, we’ve [disabled the “red screen of death”](https://github.com/flutter/flutter/issues/40452) that appears when one of your widgets is incorrectly configured. Many of you told us you’d like to see a less… prominent message in the rare situation that you ship an application with a bug, and hopefully this changed default behavior is a better alternative. (For those of you who want something else, you can use the [ErrorWidget.builder](https://api.flutter.dev/flutter/widgets/ErrorWidget/builder.html) property to further customize this.) + +It’s exciting to see that, as of the time of writing, **two-thirds of you have already upgraded to 1.17**. We’d encourage the rest of you to upgrade at your earliest convenience to take advantage of all that we’ve got to offer in this release! + +In the intervening period, we’ve shipped one hotfix release for Flutter 1.17, and we plan further updates as required to improve stability and quality. You can find out which bug fixes are included in each hotfix release as well as learn how to ensure that you’re running the latest stable version by going to the [relevant wiki page](https://github.com/flutter/flutter/wiki/Hotfixes-to-the-Stable-Channel). + +## Adobe XD Plugin Preview Availability + +Another major piece of news for designers is the [first public preview of Adobe XD support for Flutter](https://medium.com/flutter/announcing-adobe-xd-support-for-flutter-4b3dd55ff40e), published by Adobe themselves. + +This week, Adobe released an update to the plugin that [fixes a few opacity-related export bugs](https://github.com/AdobeXD/xd-to-flutter-plugin/blob/master/CHANGELOG.md) and aggressively combines shapes for cleaner Dart code. In addition, Adobe [shipped an XD update last week](https://theblog.adobe.com/xd-may-2020-offline-coediting-sharing-improvements/) that [includes new APIs for access to responsive layout](https://adobexdplatform.com/plugin-docs/changes.html#new-api-features). Once this is rolled out more broadly, stay tuned for a further update to the XD to Flutter plugin in the next few weeks that will improve flexibility of the generated Dart code. + +## Plugins and Flutter Favorites + +Most recently, last week we [announced our plans to take many of the Flutter team’s plugins to 1.0](https://medium.com/flutter/flutter-package-ecosystem-update-d50645f2d7bc), in addition to a new set of Flutter Favorites that includes [Sign in with Apple](https://pub.dev/packages/sign_in_with_apple). + +But it’s always worth calling out some of the unsung heroes. With over 10,000 packages, it’s tough to recognize them all, but this week we’re going to highlight an informal top ten list of cool packages that aren’t (yet) Flutter Favorites but are nevertheless worthy of attention: + +1. [fl_chart](https://pub.dev/packages/fl_chart) offers beautiful business charts, including line and bar charts, scatter plots, and pie charts. + +1. [flutter_blurhash](https://pub.dev/packages/flutter_blurhash) provides quick placeholders for images that may take time to download. + +1. [font_awesome_flutter](https://pub.dev/packages/font_awesome_flutter) offers thousands of icons for easy inclusion in your Flutter app. + +1. [fab_circular_menu](https://pub.dev/packages/fab_circular_menu) provides an attractive popup menu. + +1. [flutter_staggered_animations](https://pub.dev/packages/flutter_staggered_animations) offers beautiful load animations for listviews, gridviews and columns. + +1. [device_preview](https://pub.dev/packages/device_preview) constrains your app to a custom device shell, allowing you, for example, to preview an iPhone output from macOS. + +1. [smooth_page_indicator](https://pub.dev/packages/smooth_page_indicator) allows you to transition through a carousel of infocards. + +1. [flame](https://pub.dev/packages/flame) provides a small Flutter game engine with a large set of tutorials. + +1. [flutter_launcher_icons](https://pub.dev/packages/flutter_launcher_icons) is a popular CLI tool which greatly simplifies the task of updating your Flutter app’s launcher icon. + +1. [smooth_star_rating](https://pub.dev/packages/smooth_star_rating) offers a compact control for selecting a star rating. + +<DashImage figure src="images/1VB60_VnsDmTKBsewFgus_Q.webp" alt="Some of the over 10,000 plugins on pub.dev that add everything from business charts, to 2D games engine support to Flutter." caption="Some of the over 10,000 plugins on pub.dev that add everything from business charts, to 2D games engine support to Flutter." /> + + +By the way, if you’re writing a package, make sure you haven’t missed our [article on tips and tricks for developing modern plugins](https://medium.com/flutter/modern-flutter-plugin-development-4c3ee015cf5a). And please fill out our [quarterly survey](https://google.qualtrics.com/jfe/form/SV_5oNFjVJWGRECS3z?Source=TwoMonths), which has a module focused on plugin development. + +## Flutter Day + +And last, but not least — we recently [announced Flutter Day](https://medium.com/flutter/save-the-date-flutter-day-june-25-2020-8e9f5fd03248), which will take place in just a few weeks time on June 25th. We’ll have three technical sessions, #AskFlutter, and a set of new and updated codelabs to work through, getting you ready for the community-led [#Hack20 hackathon](https://flutterhackathon.com/#/) event that kicks off in the weekend that follows. + +## More Good News To Come + +We’re not done: we have a number of announcements still in the pipeline as part of [#FlutterGoodNewsWednesday](https://twitter.com/hashtag/fluttergoodnewswednesday). So keep tuning in, and if you have some good news of your own to share with us, please don’t hesitate to do so. + +You can reach us at any time on Twitter at [@flutterdev](https://twitter.com/flutterdev). We’d love to hear from you! \ No newline at end of file diff --git a/sites/www/content/blog/understanding-async-in-flutter-tests/images/0-1m66Jxnec3s8Qnb.webp b/sites/www/content/blog/understanding-async-in-flutter-tests/images/0-1m66Jxnec3s8Qnb.webp new file mode 100644 index 0000000000..e2a37ac271 Binary files /dev/null and b/sites/www/content/blog/understanding-async-in-flutter-tests/images/0-1m66Jxnec3s8Qnb.webp differ diff --git a/sites/www/content/blog/understanding-async-in-flutter-tests/index.md b/sites/www/content/blog/understanding-async-in-flutter-tests/index.md new file mode 100644 index 0000000000..3f7f34523c --- /dev/null +++ b/sites/www/content/blog/understanding-async-in-flutter-tests/index.md @@ -0,0 +1,202 @@ +--- +title: "Understanding Async in Flutter Tests" +description: "Taking a look at the caveats and best practices of working with asynchronous behavior in your Flutter Widget Tests." +publishDate: 2019-05-02 +author: arbullard +category: tutorial +layout: blog +--- + +The [FakeAsync](https://docs.flutter.io/flutter/quiver.testing.async/FakeAsync-class.html) class (from the quiver package), was created to make time-dependent code testable. Flutter unit tests run in a **FakeAsync** Zone. What does that mean? From the docs on **FakeAsync**: +> *Test code can be passed as a callback to run, which causes it to be run in a Zone which fakes timer and microtask creation, such that they are run during calls to elapse which simulates the asynchronous passage of time.* + +This means that when you use any language feature that requires the creation of timers or microtasks, like Futures and Streams, they are handled by **FakeAsync** in a way that mimics the semantics of real asynchronicity in a logical fashion, but divorced from actual wall clock time. + +Observe the code sample below: + +```dart +import 'package:flutter_test/flutter_test.dart'; + +void main() { + Future<int> outerF; + final Duration delay = Duration(seconds: 20); + + group('Async Tests - Fake Async', () { + setUpAll(() async { + outerF = Future<int>.delayed(delay, () => 42); + final Stopwatch waiting = Stopwatch()..start(); + final Future<void> innerF = Future<int>.delayed(Duration(seconds: 3), () {}); + await innerF; + waiting.stop(); + print('Waited for ${waiting.elapsed}'); + }); + // Each testWidgets callback is run in a distinct FakeAsync zone + testWidgets('Delayed in test, no time added', (WidgetTester tester) async { + // This will fail due to blowing past the 2 second test case timeout. + final Future<int> f = Future<int>.delayed(delay, () => 1); + await f; + }); + testWidgets('Delayed in test, time added', (WidgetTester tester) async { + final Future<int> f = Future<int>.delayed(delay, () => 1); + // So we extend the "time" allowed in the zone, and now it passes. + // Note that this still runs almost immediately. + tester.pump(delay); + await f; + }); + testWidgets('Delayed from setUp, no time added', (WidgetTester tester) async { + // This will fail, and will appear to be from a timeout. + await outerF; + }); + testWidgets('Delayed from setUp, time added', (WidgetTester tester) async { + // Even with the "time" added though, it will fail. It will not resolve the + // future from a different, Real Async, Zone. The error will still look like + // an exception related to a timeout. + // It will also run very slow, at least as long as the amount of "time" the + // tester binding thinks it will take. + tester.binding.addTime(delay); + await outerF; + }); + }); +} +``` + +What is happening here? + +The most fundamental takeaway is that a **FakeAsync** Zone is created for each test case. These Zones are completely independent of each other in the sense of keeping track of time(obviously, variable and references still obey the Dart scoping rules). They cannot access asynchronous work from other Zones, Real or Fake. + +The same applies to your Widget code when using **tester.pumpWidget()**. Any asynchronous behavior in your Widget will not be waited for. This presents a number of issues when testing widget trees that contain Images. + +Images are problematic for two reasons: + +## The asset manifest is not created for tests. + +So there is no way for the **AssetBundle** to load the bytes for your (Asset) image. Luckily, there is a solution for this: create your own subtype of **AssetBundle** that loads the bytes for all images before you run your tests. We have the pattern for that below. This solves the first exception you would receive in the preceding example. + +```dart +class TestAssetBundle extends CachingAssetBundle { + TestAssetBundle(Map<String, List<String>> assets) : _assets = assets { + for (String assetList in assets.keys) { + for (String asset in assets[assetList]) { + _assetMap[asset] = bytesForFile(asset); + } + } + } + + final Map<String, ByteData> _assetMap = <String, ByteData>{}; + final Map<String, List<String>> _assets; + + @override + Future<ByteData> load(String key) { + if (key == 'AssetManifest.json') { + return Future<ByteData>.value(bytesForJsonLike(_assets)); + } + return Future<ByteData>.value(_assetMap[key]); + } +} + +ByteData bytesForJsonLike(Map<String, dynamic> jsonLike) => ByteData.view( + Uint8List.fromList(const Utf8Encoder().convert(json.encode(jsonLike))) + .buffer); + +ByteData bytesForFile(String path) => ByteData.view( + Uint8List.fromList(File('../' + path).readAsBytesSync()).buffer); +``` + +This class is generally applicable. You build the bundle as follows: + +```dart +final AssetBundle bundle = TestAssetBundle(<String, List<String>>{ +'assets/dart-logo.png': <String>['assets/dart-logo.png'], +}); +``` + +You can include, in the list for each entry, a number of assets for different pixel densities. The Flutter tester has a fixed pixel density, so you should normally only need to provide a single item there. + +## Calls to Flutter Engine + +The operation to decode the bytes into a paint-able image comes from the Flutter Engine through a call to a native function. This is an asynchronous operation, and again, the tester will not wait for it to complete. There is nothing you can do in a **FakeAsync** Zone to work around this. + +This leads to some **iron laws** around testing widget trees that contain images: + +1. Use **DefaultAssetBundle** to load the bytes for the images that will be used by tests in that file. + +1. In a **FakeAsync** Zone, i.e., in normal widget tests, do not rely on implicit size or presence of images in any sort of assertion. If such an assertion must be made, wrap your image in a **SizedBox** or some other mechanism to have deterministic control over the widgets size. + +1. If you don’t know the size of the image at development time (maybe it comes from user content and may vary), you may need to test with integration or end-to-end tests. It’s worth thinking about what you are looking to understand about the widget being tested. + +1. If you are looking for visual regression testing, there are options available that we’ll discuss in the section below. + +## Is it possible to have real async semantics in unit tests? + +The short answer is yes, but the technique described here should be used only in very specific contexts. Below, we will discuss a set of best practices around the technique. + +The solution is actually rather easy, as shown in the following example: + +```dart +import 'dart:async'; +import 'package:flutter_test/flutter_test.dart'; + +void main() async { + LiveTestWidgetsFlutterBinding(); + Future outsideFuture; + + setUp(() { + outsideFuture = Future.microtask(() { + return 'outside'; + }); + }); + + testWidgets("awaiting future with value from setUp works", (t) async { + Future insideFuture = Future.microtask(() { + return 'inside'; + }); +} +``` + +This code binds the **TestWidgetsFlutterBinding** to a **LiveTestWidgetsFlutterBinding**, which runs test code in a real async zone. This means if you await an asynchronous event that takes 10 minutes, your test runs for at least 10 minutes. If the event you are awaiting has non-deterministic performance, so will your tests. + +Unfortunately, while this is available to you, it offers you very little benefit when using flutter test. The reason that most developers will want to take advantage of **LiveTestWidgetsFlutterBinding** is when they are trying to understand behavior in their tests. Luckily, there is an easier way to access this binding mode that is also much more helpful. You can run your test *on device* using flutter run path/to/your_test.dart. + +One caveat with flutter running your test case is that **matchesGolden** assertions will cause an exception to be thrown. A good practice is to place [Goldens tests](https://docs.flutter.io/flutter/flutter_test/matchesGoldenFile.html) in a separate directory, and know that those tests should only be called with flutter test. + +## What does this mean for images in my tests? + +This mode does not change the fact that Widget Tests are not the place to make layout assertions, besides the most basic cases. If your layout depends on Images or other assets of a size that is unknown at development time, your assertions will not work as expected. + +If you *do* know the size of the asset(s) at development time and wish to make layout assertions (like **isOnstage**), we recommend wrapping the widget in a **ConstrainedBox**, or **SizedBox** to ensure that the Widget will occupy the space you expect in your layout. + +## Key Takeaways + +Testing your widgets is critical in the maintainability of your application. Unit tests are the fastest and most productive way to test your code and **testWidgets** provides you with some amazing facilities for unit testing the most visible part of your application. That being said, this article shows that there are some caveats to be aware of and some best practices to follow. + +## Caveats + +* **testWidgets** is for testing *Widget Behavior*, not Widget appearance or layout. + +* **FakeAsync** has semantics to make your tests run fast, but it requires changing the way you think about your app. + +* The async code that runs in **tester.runAsync**, and within a test case, run in real time. + +* The async code from outside the test case, and within your Widget tree, run in simulated time. + +* You can not cross between these Zones. + +* Your test code should not depend on asynchronicity in your Widget tree. + +* Use tester.pump and **tester.pumpAndSettle** to trigger frames and ensure that animations have completed. + +## Best practices + +* Use driver tests for integration testing + +* Put them in a driver_test/ directory. These are time-intensive and expensive, and this allows you to only run them when needed. + +* Use goldens tests to prevent regressions in your layouts and widget appearance. + +* Put them in a separate directory within test/(or outside of it). Run them only when you mean too. Use — update-goldens to capture intended changes. + +* Don’t depend on HTTP calls, file loading, images, and other sources of asynchronicity in Widget Tests. + +* If you can’t escape **AssetImage** loading, use a **TestAssetBundle**. Let us know in the comments if you’d like to see this in a pub package. + +With these tools in hand, you should be ready to start adding more test coverage to your Flutter Application. Happy Fluttering! \ No newline at end of file diff --git a/sites/www/content/blog/unleash-new-ai-capabilities-for-flutter-in-firebase-studio/images/0CHTpeFMydBD0KQY9.webp b/sites/www/content/blog/unleash-new-ai-capabilities-for-flutter-in-firebase-studio/images/0CHTpeFMydBD0KQY9.webp new file mode 100644 index 0000000000..05cdd4145d Binary files /dev/null and b/sites/www/content/blog/unleash-new-ai-capabilities-for-flutter-in-firebase-studio/images/0CHTpeFMydBD0KQY9.webp differ diff --git a/sites/www/content/blog/unleash-new-ai-capabilities-for-flutter-in-firebase-studio/images/1FPNH3GWzShcGYInVxc30qA.webp b/sites/www/content/blog/unleash-new-ai-capabilities-for-flutter-in-firebase-studio/images/1FPNH3GWzShcGYInVxc30qA.webp new file mode 100644 index 0000000000..3458c4e6e7 Binary files /dev/null and b/sites/www/content/blog/unleash-new-ai-capabilities-for-flutter-in-firebase-studio/images/1FPNH3GWzShcGYInVxc30qA.webp differ diff --git a/sites/www/content/blog/unleash-new-ai-capabilities-for-flutter-in-firebase-studio/images/1i5KGoCCzGQXV5SuTYhbpbw.webp b/sites/www/content/blog/unleash-new-ai-capabilities-for-flutter-in-firebase-studio/images/1i5KGoCCzGQXV5SuTYhbpbw.webp new file mode 100644 index 0000000000..b81e2b5a6d Binary files /dev/null and b/sites/www/content/blog/unleash-new-ai-capabilities-for-flutter-in-firebase-studio/images/1i5KGoCCzGQXV5SuTYhbpbw.webp differ diff --git a/sites/www/content/blog/unleash-new-ai-capabilities-for-flutter-in-firebase-studio/images/1lPy6kRkj2N5ybEhHIKjbVw.webp b/sites/www/content/blog/unleash-new-ai-capabilities-for-flutter-in-firebase-studio/images/1lPy6kRkj2N5ybEhHIKjbVw.webp new file mode 100644 index 0000000000..c05da298c9 Binary files /dev/null and b/sites/www/content/blog/unleash-new-ai-capabilities-for-flutter-in-firebase-studio/images/1lPy6kRkj2N5ybEhHIKjbVw.webp differ diff --git a/sites/www/content/blog/unleash-new-ai-capabilities-for-flutter-in-firebase-studio/index.md b/sites/www/content/blog/unleash-new-ai-capabilities-for-flutter-in-firebase-studio/index.md new file mode 100644 index 0000000000..ed886f5918 --- /dev/null +++ b/sites/www/content/blog/unleash-new-ai-capabilities-for-flutter-in-firebase-studio/index.md @@ -0,0 +1,59 @@ +--- +title: "Unleash new AI capabilities for Flutter in Firebase Studio" +description: "Flutter development just got a lot smarter in Firebase Studio! At I/O Connect India, we unveiled new AI-powered features designed to give…" +publishDate: 2025-07-23 +author: antfitch +image: images/1lPy6kRkj2N5ybEhHIKjbVw.webp +category: spotlight +layout: blog +--- + +Flutter development just got a lot smarter in Firebase Studio! At I/O Connect India, we unveiled new AI-powered features designed to give you a more automated and productive workflow. In this post, we’ll dive into the new AI enhancements specifically for Flutter in Firebase Studio, and show you how you can use them to build powerful applications like the demo we showcased on stage. + +## Upgraded AI-optimized Flutter template + +The Flutter template in [Firebase Studio](https://firebase.studio/) received a significant upgrade. With its new AI capabilities, you can optimize your workspace for an AI-first experience. For example, when building the I/O Connect India demo with this new template, Firebase Studio generated a multi-screen, e-commerce app complete with state management, Firebase authentication, products, a cart, and checkout. The AI-optimized template defaults to autonomous Agent mode, allowing Gemini to make changes directly without waiting for approval. We’ve also incorporated rules for Gemini into an (automatically generated) `airules.md` file to further refine the code Gemini generates, enabling you to add features or generate an entire Flutter app in a more streamlined flow. + +<DashImage figure src="images/1lPy6kRkj2N5ybEhHIKjbVw.webp" alt="The Flutter template in Firebase Studio has been upgraded for an AI-first experience" caption="The Flutter template in Firebase Studio has been upgraded for an AI-first experience" /> + + +## Autonomously make changes + +Previously, when working on a Flutter project in Firebase Studio, you would manually approve each code change in Agent mode. This workflow has evolved significantly and can make edits autonomously (independently and automatically). With the new Agent (Auto-run) mode, Gemini can now autonomously make changes, generate entire apps, and add features in a streamlined flow. For instance, when developing an app similar to our I/O Connect India demo, you can use Agent (Auto-run) mode to seamlessly integrate Firebase backend services and implement complex navigation patterns in a single interaction. Given Gemini’s robust support for Dart and Flutter, this autonomous mode keeps you in the development flow. And if a change isn’t exactly what you intended, rest assured that Firebase Studio leverages Git, allowing you to quickly revert your changes to a previous commit. + +<DashImage figure src="images/1i5KGoCCzGQXV5SuTYhbpbw.webp" alt="Agent (Auto-run) mode with a Flutter project in Firebase Studio" caption="Agent (Auto-run) mode with a Flutter project in Firebase Studio" /> + + +## AI rules + +With the upgraded Flutter template, we’ve incorporated AI instructions into a new file: `airules.md`. This file (located at the root of your project), contains explicit rules for Gemini to follow as it generates code, enhancing the quality and relevance of the output. You have the flexibility to either use the default AI rules or customize them to fit your project’s specific needs. While there are numerous rules, here are a few examples: + +* Act as a Flutter co-developer + +* Writes unit tests + +* Proactively look for and fix errors + +* Choose which themes, tools, extensions, and startup commands to use + +* Add and remove Flutter packages + +* Adhere to Flutter and Dart best practices for code quality + +* Set up complex navigation + +<DashImage figure src="images/1FPNH3GWzShcGYInVxc30qA.webp" alt="Overview of the default Flutter AI rules (airules.md) in Firebase Studio" caption="Overview of the default Flutter AI rules (airules.md) in Firebase Studio" /> + + +## Compatible with Dart MCP Server + +Firebase Studio now includes foundational support for the Model Context Protocol (MCP), and we’re excited to share that it works with our [Dart MCP Server](https://dart.dev/tools/mcp-server). When integrated within Firebase Studio, the Dart MCP Server empowers Gemini in Firebase Studio to analyze and automatically fix Dart and Flutter-specific errors in your project’s code. You can also efficiently search pub.dev for optimal packages, manage dependencies in `pubspec.yaml`, run tests, and much more. For example, when building a Flutter app similar to the one we demoed on stage at I/O Connect India, Gemini should identify and autonomously correct both static and runtime errors. Currently, Dart MCP Server is in beta. + +To learn how you can use the Dart MCP Server while building your Flutter app in Firebase Studio, see the [Supercharge Your Dart & Flutter Development Experience with the Dart MCP Server](https://medium.com/flutter/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server-2edcc8107b49) blog post. + +<DashImage figure src="images/0CHTpeFMydBD0KQY9.webp" alt="Dart MCP Server configuration for a Flutter app in Firebase Studio" caption="Dart MCP Server configuration for a Flutter app in Firebase Studio" /> + + +## Build with us + +We have ambitious plans for the continued integration of Firebase Studio with Flutter, and we’re eager to [hear your feedback on using Flutter in Firebase Studio](https://community.firebasestudio.dev/) as you explore these new capabilities. We’re incredibly excited to see what you create! \ No newline at end of file diff --git a/sites/www/content/blog/updates-on-flutter-and-firebase/images/0UYxGBLGPhiFgOv2j.webp b/sites/www/content/blog/updates-on-flutter-and-firebase/images/0UYxGBLGPhiFgOv2j.webp new file mode 100644 index 0000000000..014b19ee0e Binary files /dev/null and b/sites/www/content/blog/updates-on-flutter-and-firebase/images/0UYxGBLGPhiFgOv2j.webp differ diff --git a/sites/www/content/blog/updates-on-flutter-and-firebase/images/0kZUvqusPkrzKt54k.webp b/sites/www/content/blog/updates-on-flutter-and-firebase/images/0kZUvqusPkrzKt54k.webp new file mode 100644 index 0000000000..e0332a55d1 Binary files /dev/null and b/sites/www/content/blog/updates-on-flutter-and-firebase/images/0kZUvqusPkrzKt54k.webp differ diff --git a/sites/www/content/blog/updates-on-flutter-and-firebase/images/0v4wFYjvXB2iCem55.webp b/sites/www/content/blog/updates-on-flutter-and-firebase/images/0v4wFYjvXB2iCem55.webp new file mode 100644 index 0000000000..fe62f1d1a2 Binary files /dev/null and b/sites/www/content/blog/updates-on-flutter-and-firebase/images/0v4wFYjvXB2iCem55.webp differ diff --git a/sites/www/content/blog/updates-on-flutter-and-firebase/index.md b/sites/www/content/blog/updates-on-flutter-and-firebase/index.md new file mode 100644 index 0000000000..8565c9eb0c --- /dev/null +++ b/sites/www/content/blog/updates-on-flutter-and-firebase/index.md @@ -0,0 +1,68 @@ +--- +title: "Updates on Flutter and Firebase" +description: "Updated Firebase plugins for higher quality and a new site with Flutter-specific docs, snippets and videos" +publishDate: 2020-08-19 +author: csells +image: images/0v4wFYjvXB2iCem55.webp +category: announcements +layout: blog +--- + +### *Updated Firebase plugins for higher quality and a new site with Flutter-specific docs, snippets, and videos* + +Flutter is more than just an engine, a set of widgets, and some tools; it also includes a large ecosystem of packages to add functionality to your apps beyond what comes out of the box. Some of the most popular packages are the set that supports Firebase (aka FlutterFire). As part of our continued focus on quality for all aspects of Flutter, [we recently announced](https://medium.com/flutter/flutter-package-ecosystem-update-d50645f2d7bc) that Invertase, a consulting firm with deep expertise in Dart, Flutter, and Firebase, would be taking over the bulk of the development and maintenance of the FlutterFire plugins. In just the short amount of time since that announcement, they’ve made a tremendous amount of progress, including a new set of Core, Authentication, and Firestore plugins that provide new functionality and align the APIs more closely with other Firebase SDKs. In addition, they’ve created a whole site of Flutter-specific documentation for each of the Firebase plugins that includes installation instructions and code snippets for common scenarios, as well as a migration guide to help you move your code to this new version. And finally, in addition to all of that great work, this release represents 300+ issues resolved and 80+ pull requests merged. + +<DashImage figure src="images/0kZUvqusPkrzKt54k.webp" /> + + +## Principles + +Historically, the Firebase plugins for Flutter grew organically due to passionate team members contributing when they could. With the transfer of plugin engineering to Invertase, the Flutter team and Invertase together took the opportunity to reexamine the basics. This exercise led to a number of principles: + +* **Features will be consistent with native Firebase SDKs**, like the ability to access the currently logged-in user synchronously instead of asynchronously. + +* **Naming will be consistent with native Firebase SDKs**, like the use of *get()* instead of *getDocuments().* + +* All plugins will **depend on firebase_core and consistently require initialization** using *Firebase.initializeApp().* + +* All plugins will **depend on the same underlying native Firebase SDK version.** + +* All plugins will be **updated to use [federation](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#federated-plugins)**, making it possible to support a wide range of platforms and not just mobile. + +* All plugins will require **explicit adoption of the new *firebase_core*** version, so that running *flutter pub update* won’t break your code. + +* **Old names will be deprecated** to ease adoption as plugins are updated to meet these principles. + +* **Important issues will be fixed and a full set of tests** will be created for each set of plugins as they are updated. + +The goal of these principles is to make for a better experience for new and existing Firebase users on Flutter. We’re also working hard to make sure that you’re able to adopt the changes to each set of 3-4 plugins as we release them instead of waiting for the whole set of 15 plugins to be updated. + +## Migration + +Migration to the new [Core](https://firebase.flutter.dev/docs/core/usage), [Authentication](https://firebase.flutter.dev/docs/auth/overview), and [Firestore](https://firebase.flutter.dev/docs/firestore/overview) plugins is largely a matter of updating the versions in your *pubspec.yaml* file, pulling down those versions with *flutter pub get*, and adding a call to [*Firebase.initializeApp()*](https://firebase.flutter.dev/docs/overview#initializing-flutterfire). Once you do that, you may also choose to deal with the set of names that have been deprecated, although the old methods and properties will continue to work for now. + +Not all of the work associated with these principles has been completed yet (in fact, we’re just getting started), but if you do adopt the new Core, you can continue to use the rest of the plugins that haven’t had any major changes yet; even plugins, like Storage and Messaging, have been updated to depend on the new Core. This means that you’ll get a more consistent experience across the board now with more changes coming that will just make things better. + +For the full list of changes for the new versions of Core, Authentication, and Firestore, as well as detailed instructions on how to bring your Firebase code forward, I recommend that you [read the Migration Guide](https://firebase.flutter.dev/docs/migration). + +## Platform support + +The work that Invertase is doing is currently focused on Android and iOS, with macOS thrown in since it shares 99% of the same code with iOS. You can see the support for the Firebase plugins at the new documentation site for FlutterFire: [https://firebase.flutter.dev/](https://firebase.flutter.dev/) + +<DashImage figure src="images/0v4wFYjvXB2iCem55.webp" alt="Current status of the Firebase plugins for Flutter" caption="Current status of the Firebase plugins for Flutter" /> + + +You’ll notice that while every Firebase plugin supports mobile, which includes Android and iOS, not all of them support macOS. We’ll bring that support along as we work our way through the list. You’ll also notice that several of the plugins already support Flutter on the web as well as mobile. The breadth of support will continue to grow as work continues. + +## Documentation + +In addition to providing a comprehensive migration guide and showing platform support for each plugin, we’ve also provided the first draft at a Flutter-centric set of documentation at [https://firebase.flutter.dev/docs](https://firebase.flutter.dev/docs). + +<DashImage figure src="images/0UYxGBLGPhiFgOv2j.webp" /> + + +No more reading the native Android or web Firebase docs and using that to guess the equivalent APIs to use in your Flutter apps! On this site, you’ll find docs for most of the Firebase plugins (many more than just Core, Authentication, and Firestore), Android, iOS, web-specific installation instructions, an overview of common scenarios with code snippets, and videos. + +## What’s next? + +Today we’ve announced the first round of updates for the Core, Authentication, and Firestore plugins. To make sure that we’re working on the highest impact plugins for Flutter developers, our next round will likely be Crashlytics, Functions, Messaging, and Storage. Once those are in shape, we’ll circle back and make sure that web support for those 7 plugins lives up to our principles as well. This is a first big step on a journey to ensure that Flutter developers have the full range of services that other Firebase SDKs have to offer. If we make any missteps along the way, [please let us know](https://github.com/firebaseextended/flutterfire/issues). \ No newline at end of file diff --git a/sites/www/content/blog/updates-on-flutter-testing/images/0PEc27voy2YR_winG.webp b/sites/www/content/blog/updates-on-flutter-testing/images/0PEc27voy2YR_winG.webp new file mode 100644 index 0000000000..f52a554589 Binary files /dev/null and b/sites/www/content/blog/updates-on-flutter-testing/images/0PEc27voy2YR_winG.webp differ diff --git a/sites/www/content/blog/updates-on-flutter-testing/images/0aUliGJHIQ51Djk84.webp b/sites/www/content/blog/updates-on-flutter-testing/images/0aUliGJHIQ51Djk84.webp new file mode 100644 index 0000000000..a31cf13f90 Binary files /dev/null and b/sites/www/content/blog/updates-on-flutter-testing/images/0aUliGJHIQ51Djk84.webp differ diff --git a/sites/www/content/blog/updates-on-flutter-testing/images/0hurt3UPLzo0bnzMi.webp b/sites/www/content/blog/updates-on-flutter-testing/images/0hurt3UPLzo0bnzMi.webp new file mode 100644 index 0000000000..eee80feebe Binary files /dev/null and b/sites/www/content/blog/updates-on-flutter-testing/images/0hurt3UPLzo0bnzMi.webp differ diff --git a/sites/www/content/blog/updates-on-flutter-testing/index.md b/sites/www/content/blog/updates-on-flutter-testing/index.md new file mode 100644 index 0000000000..35eb0deac3 --- /dev/null +++ b/sites/www/content/blog/updates-on-flutter-testing/index.md @@ -0,0 +1,120 @@ +--- +title: "Updates on Flutter Testing" +description: "Updated integration testing that supports Firebase Test Lab on mobile; stand-alone support for web and desktop, too!" +publishDate: 2020-12-09 +author: csells +image: images/0PEc27voy2YR_winG.webp +category: announcements +layout: blog +--- + +Flutter’s goal is to provide an open framework for quickly building beautiful native apps on multiple platforms. A large part of achieving that goal is the ability for you to easily test your Flutter apps to make sure that they work and look the way you want them to on the platforms you’re targeting. Some of that testing can be handled by unit tests written using [the unit test framework built into Dart](https://dart.dev/guides/testing). Dart unit testing works exceedingly well for non-UI testing; it runs on your development machine and doesn’t rely on the GUI of your Flutter app. + +Integration testing (also called end-to-end testing or GUI testing) is a step up from unit testing because integration testing attempts to simulate a user interacting with your app by doing things like pushing buttons, selecting items, and typing on the keyboard. This testing is automated, to avoid having humans do that kind of repetitive work because, frankly, we’re not that great at it. Our initial solution to avoiding human interaction was a special way to write Flutter tests. These tests are run from the host like Dart unit tests, and drive the app running on a real or virtual device, just as a person would. These kinds of tests were called Flutter driver tests because you use the `flutter_driver` package and the `flutter drive` command line to drive the GUI of your app. + +Unfortunately, Flutter driver tests have a number of issues. One issue is that tests run from the development machine and communicate with the app on the device, which means that the tests aren’t suitable for running on a device farm like [Firebase Test Lab](https://firebase.google.com/docs/test-lab). Another issue is that having a separate process for your tests makes it difficult to check the state of your app. You can check the output, but how do you know, for example, the internal state of the app. And, finally, the flutter_driver API is more complicated than it needs to be, particularly when it comes to writing the code to find the appropriate widgets on the screen to test against. + +And so, to solve these problems and continue to improve the Flutter testing experience across a growing number of targets, we’re happy to announce version 1.0 of [the integration_test plugin](https://pub.dev/packages/integration_test), a simpler way to test your Flutter apps that also supports Firebase Test Lab. + +## Getting started with integration_test + +Using the `integration_test` plugin requires two steps. First, add the plugin to your `pubspec.yaml` file as a development dependency, and use `flutter pub get` to pull the plugin into your project: + +``` +# pubspec.yaml +… +dev_dependencies: + flutter_test: + sdk: flutter + integration_test: ^1.0.0 +… +``` + + +Then, use the`integration_test` package in your test code: + +```dart +// widget_integration_test.dart +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:hello_world/main.dart'; +import 'package:integration_test/integration_test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the + icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} + +``` + +Notice the import of the `integration_test` package and the call to `IntegrationTestWidgetsFlutterBinding.ensureInitialized()`, which ensures that the package is properly initialized. Also notice the standard `WidgetTester` test code. This is the same test code generated by the default Counter app template during `flutter create`. + +While `integration_test` enables your tests to be bundled into your app in a stand-alone way (which is a requirement for device farms like Firebase Test Lab), it requires some build magic that we’ll cover below. During the testing development process, however, it’s handy to just be able to run your tests interactively via the command line and for that, you’ll need a new entry point: + +```dart +// integration_test.dart +import 'package:integration_test/integration_test_driver.dart'; +Future<void> main() => integrationDriver(); +``` + +This call to `integrationDriver` is a convenience wrapper around the `integrationDriver` plugin to enable you to run your new tests with the `flutter drive` command: + +``` +$ flutter drive \ + --driver=test_driver/integration_test.dart \ + --target=test/widget_integration_test.dart +``` + + +This command deploys your app to the simulator, executes your tests, and shows you the results. + +<DashImage figure src="images/0hurt3UPLzo0bnzMi.webp" alt="Flutter integration_test in action" caption="Flutter integration_test in action" /> + + +This particular test is running on iOS, but the `integration_test` plugin also works for Android by changing the`--device-id` option, as appropriate. Furthermore, you can run your integration tests against the web and desktop targets, too, although this functionality is still is pre-release. + +## Running on Firebase Test Lab + +After you know that your tests work locally using either virtual or physical hardware, you can set your app loose on the plethora of devices available through Firebase Test Lab. + +<DashImage figure src="images/0PEc27voy2YR_winG.webp" alt="Firebase Test Lab console" caption="Firebase Test Lab console" /> + + +Firebase Test Lab is a cloud-based, app testing infrastructure. With one operation, you can test your Android or iOS app across a wide variety of devices and device configurations, and see the results — including logs, videos, and screenshots — in the Firebase console. One of the major advancements of the `integration_test` plugin is the ability to run your Flutter apps targeting Android and iOS on Firebase Test Lab, giving you the ability to test across hundreds of devices simultaneously in order to find platform, form factor, or device-specific issues before shipping your app. + +To run a test on Firebase Test Lab, you need to do some configuration and use Gradle commands to build an instrumentation test for both Android and iOS, as appropriate. For those details, check out [the new integration test documentation on flutter.dev](http://flutter.dev/docs/testing/integration-tests). + +## Migrating from Flutter driver tests + +For those of you with existing `flutter_driver` tests, moving to the new API isn’t too difficult. In addition to the appropriate initialization that was previously mentioned, you also need to move to the new `WidgetTester` API. + +<DashImage figure src="images/0aUliGJHIQ51Djk84.webp" alt="flutter_driver API (left) vs. WidgetTesting API (right)" caption="flutter_driver API (left) vs. WidgetTesting API (right)" /> + + +The `flutter_driver` API (shown on the left) and the `WidgetTester` API (shown on the right) are conceptually very similar, but you can see that a lot of the details are different. For example, instead of calling the `waitFor` method on the `flutter_driver`, you call the `pumpAndSettle` method on the `WidgetTester`. The former waits for a specific widget to appear, whereas the latter waits for the app’s UI rendering phase to settle. After you have a specific widget, you act on it similarly with the two APIs, but you use different objects. The `WidgetTest` API is more in-line with what you’re used to seeing in Dart unit tests. As you can see, the `expect` method is used to ensure that the contents of a widget are what you expect. + +For the details about how to move your tests from flutter_driver to integration_test and WidgetTester, see [the migration docs on flutter.dev](http://flutter.dev/docs/testing/integration-tests#migrating-from-flutter_driver). + +## Native UI testing + +If you have an existing Android or iOS app to which you’re adding Flutter using [Add-to-App](https://flutter.dev/docs/development/add-to-app), then you’re likely to have existing integration tests for those native apps that you want to leverage. In that case, add the tests for your Flutter screens to those existing tests. For Android, you can add the tests if they’re written using the Espresso framework using [the espresso plugin](https://pub.dev/packages/espresso), which provides bindings for Espresso tests of Flutter Android apps. We’ll have a similar plugin to support native iOS tests created using Earl Grey soon. + +## Summary + +This announcement presents a new foundation for integration testing using the new `integration_test` plugin for Flutter. Not only is the API simpler and more consistent, tests written using `integration_test` can run on hundreds of different devices using the Firebase Test Lab. The Flutter team plans to build on this new foundation by updating the `flutter create` template to use `integration_test` out of the box, adding updates to the test output to support existing testing tools using the `JUnit` format, adding the ability to take screenshots during tests for golden testing comparisons, and more. For the full list of details about our new integration testing recommendations for your Flutter apps and plugins, see [the testing docs on flutter.dev](http://flutter.dev/docs/testing/integration-tests). \ No newline at end of file diff --git a/sites/www/content/blog/vibe-once-run-anywhere-with-antigravity-and-flutter/images/1Mg2Bj5FBg1I19r1Jw4q9Kg.webp b/sites/www/content/blog/vibe-once-run-anywhere-with-antigravity-and-flutter/images/1Mg2Bj5FBg1I19r1Jw4q9Kg.webp new file mode 100644 index 0000000000..9b0fee449e Binary files /dev/null and b/sites/www/content/blog/vibe-once-run-anywhere-with-antigravity-and-flutter/images/1Mg2Bj5FBg1I19r1Jw4q9Kg.webp differ diff --git a/sites/www/content/blog/vibe-once-run-anywhere-with-antigravity-and-flutter/index.md b/sites/www/content/blog/vibe-once-run-anywhere-with-antigravity-and-flutter/index.md new file mode 100644 index 0000000000..f11c381679 --- /dev/null +++ b/sites/www/content/blog/vibe-once-run-anywhere-with-antigravity-and-flutter/index.md @@ -0,0 +1,85 @@ +--- +title: "Vibe once, run anywhere with Antigravity and Flutter" +description: "co-authored with Rody Davis" +publishDate: 2026-06-29 +author: craiglabenz +image: images/1Mg2Bj5FBg1I19r1Jw4q9Kg.webp +layout: blog +--- + +<DashImage figure src="images/1Mg2Bj5FBg1I19r1Jw4q9Kg.webp" /> + + +Coding agents and the way we use them has already dramatically evolved since their arrival just a handful of months ago. Initially, the focus was heavily on observation — reviewing every single output line by line. But as models rapidly increased in capability, the industry shifted toward true agentic engineering. Today, developers, PMs, and designers are wearing multiple hats, focusing on lofty conceptual goals and letting agents tackle the individual components. + +To explore this new frontier, our team wanted to build an experience that showcased this exact workflow. We wanted to build a game, generate its assets, write the marketing page, and deploy the whole thing, all using Google’s premiere AI-native platform: Antigravity. + +Antigravity brings the best of Google into one place, using a tight feedback loop of planning, acting, and verifying. It creates artifacts, writes code, runs tests, and even clicks buttons in your UI to ensure it actually completed the task correctly. + +The result of our agentic adventure is DashLander — a moonlander-style game set on procedurally generated asteroids. Here is the story of how we built it. + +### **Why Flutter in the AI era?** + +A lot of people might be thinking: *If agents can write native code, why not just have them write Android, iOS, and web apps entirely separately?* + +That is a valid question; and is what originally inspired the concept of Flutter as a **“Vibe once, run anywhere”** UI toolkit. + +Having a single source of truth is just as important for AI as it is for humans. By having an agent write a single, cross-platform app, teams can eliminate the subtle bugs that inevitably creep in between different languages or platform-specific paradigms. Furthermore, Dart’s strong typing gives LLMs excellent feedback. Languages with looser typing systems require dramatically more analysis by the LLM to know whether a given piece of code is correct across all scenarios. Flutter and Dart, on the other hand, use their analysis server to send strong signal about mismatches in function signatures or class shapes to your agent. Paired with stateful hot reload, agents are just as accelerated by Flutter as human developers. + +And, while agentic development has dramatically reduced the cost of producing new software, it’s still far from being free. With agentic development, time is money both figuratively *and* literally, as more time spent querying equals more tokens; which equals more AI spend. With Flutter, agents fresh off a successful coding task don’t have to start over to support another platform, **saving you money**. + +### **Failing fast to build better** + +We knew we wanted to build a game where you pilot a moonlander, complete with dynamic elements, custom shaders, and particle effects. (We didn’t have the heart to put Dash herself into the cold vacuum of space so, instead, you’re piloting a ship.) Integrating these concepts was, of course, vastly accelerated by using AI, but that doesn’t preclude slowing down and thoughtfully writing high quality code later on. If anything, a fast exploratory phase with AI helps you reach that stage more quickly, as many of your failed experiments can come and go within the life cycle of a few prompts rather than taking hours or days of programming just to learn whether or not a specific idea is actually much fun. And if there’s one thing in this life that doesn’t matter, it’s the code quality of an experimental feature that never sees the light of day. + +Crucially, we also knew what we *weren’t* building: a high-performance, real-time multiplayer game. Real-time multiplayer features introduce latency constraints and exponentially scale infrastructure complexity. Instead, we realized we could deliver 90% of that engaging competitive experience by replaying the ghosts of past high scores in a Challenge Mode. This allows us to rely on static storage and simple backends and to cleverly reuse perfectly written “AI opponent logic” for free, just by replaying what the best users have already done. And that’s the best code of all: the code you neither write nor maintain. + +### **Generating a universe of assets** + +To make an asteroid lander fun, we needed great assets, and we leaned heavily on the agentic ecosystem to get them: + +* **Audio:** We used Google’s Lyria to generate background music. (Though, putting on his old audio engineer hat, Rody did record custom thruster noises in his backyard with a microphone, a hose, and a spray can. What a fun afternoon!) + +* **Visuals:** We generated UI designs in Stitch and Google Canvas. Gemini wrote our particle effects directly in code and we used Nano Banana to generate the app icons. + +* **Physics:** Because this game takes place in a vacuum, we wanted accurate Reaction Control Systems (RCS). We unleashed Gemini Deep Research to find the exact zero-atmosphere physics formulas we needed, which it compiled into a Google Doc. Then we included that research in our context and Gemini translated the equations directly into Dart. Are they *actually* the right physics equations? Hilariously, we don’t know, because we’re not physicists! But the controls certainly *feel* correct when you’re playing the game. + +### **Iterating on the prototype** + +We started our build inside AI Studio’s sandbox to generate rapid, throwaway code before committing to an architecture. These rich prototypes can act as context compression for the model later, locking in micro-decisions early. Some of our prototypes had more fun gameplay, some looked significantly more appealing, and others captured our zero-G physics most cleanly. For our final game, we merged the best ideas from each prototype. + +Once we loaded the project into Antigravity, we equipped our agent with the MCP servers for Firebase, Flutter, the Flame game engine, and the Gemini API. We kicked off a fresh prompt and Antigravity split the tasks across specialized sub-agents for the backend, UI, and game logic. Within five minutes, we had a game, written in Flutter and Flame, that would have normally taken days to write. But realistically, it wasn’t anywhere near done. There were only three hard-coded levels, the camera didn’t follow the lander, and the math implied our ship was roughly the size of the Empire State Building. + +<YoutubeEmbed id="3mKKi8ZW0Yg" title="Dashlander v1" fullwidth="true"/> + + +It took about another 100 prompts to get the game to its final state. A huge percentage of those prompts were just about code organization and adding tests. Everyone has their own relationship with reading AI-generated code, but Rody and I highly value what I’ve been calling “cognitive ownership” and want to still understand the deep inner workings of our app. We read a lot of what Gemini wrote, pushing back on the LLM to refactor for clarity and reusability. + +I also had to face a harsh reality: I am not great at trigonometry, and *boy* is there a lot of it in DashLander. When the agents told me they perfectly implemented a new system like landing calculations and scoring, I’d often play the game and immediately see discrepancies. Unfortunately, no matter how much I read Gemini’s dense trigonometry in my codebase, I couldn’t pinpoint where the disconnect was hiding. To escape this Catch-22, I asked Gemini to build a top-secret debug mode behind a keyboard shortcut. It rendered overlays showing the exact terrain data, relative tilts of the surface, and collision hitboxes, allowing me to prove to myself exactly where the agent’s calculations were wrong. Armed with definitive evidence, I was able to help work with Gemini to fix problems in the files of dense math. + +<YoutubeEmbed id="cVcM8jZ_Ioo" title="Dashlander debug mode" fullwidth="true"/> + + +### **The tricky business of time travel** + +When we added Challenge Mode, Antigravity did a brilliant job wiring up the data models and connecting them to Cloud Firestore. But during testing, I noticed a sneaky bug. I would race against the ghost of a previous high score and, visually, everything looked great as the ghost ship drifted along nicely. But as the replay went on, it turned out that the ghost ship was imperceptibly desyncing from its past self. At the end of the run, despite the original player having landed perfectly, the ghost ship would sometimes violently crash into the asteroid. + +To solve this, I had to make like it was 2023 and turn on my own brain. *Scary*, I know. + +I realized that the core problem was a fundamental aspect of modern computer architecture. You see, on a modern, multi-process CPU, you can tell a program to run a piece of code at an exact millisecond in the future, but the CPU can never guarantee perfect accuracy due to the nature of process scheduling. And, unfortunately for me, a single millisecond delay in activating a thruster during a replay fundamentally alters a ship’s simulated flight path. I realized that, instead of just logging thruster timestamps, I had to store a complete representation of the lander’s physical state at the exact moment of each thruster event to correct inaccuracies during a replay. Gemini then integrated the logic to continuously compare the replay’s live simulation against these physical checkpoints, auto-correcting any micro-drifts. The result was a flawless, crash-free replay. + +### **From game to launch** + +You’ve probably felt the pressure to wear more hats in your day job, regardless of the size of your team. For example, maybe you’re great at writing games but don’t want to market them. Or, you’re a rockstar UX engineer but prefer not to handle graphic design. The good news is, you don’t have to do everything alone. + +We used Stitch to generate our marketing landing page from a single prompt, passing in screenshots of our running game as context. (Admittedly, if producing an optimized landing page had been our actual goal, we would have iterated on that a bit more.) Using a one-click export into Antigravity, we then downloaded the HTML and got to work. Because we were building in Dart, we wanted to keep our stack unified. We used [Jaspr](https://pub.dev/packages/jaspr), a web framework that maps HTML directly to Dart components, to seamlessly integrate the generated HTML. Gemini handled this conversion perfectly, allowing us to share code between our game and our marketing site while preserving industry-standard SEO indexing. + +Finally, we used the Antigravity CLI to build our Flutter web app, copy the build directory over to Jaspr, run the build script, and deploy directly to Firebase Hosting — all in one unbroken terminal workflow. + +While DashLander is currently web-first to easily validate mechanics, because it’s built with Flutter, we are always just one command away from deploying to iOS, Android, macOS, Windows, Linux, or even the infotainment panel in a Toyota RAV4. As of this writing, the game is currently available at [dashlander.com](http://dashlander.com); though if you find this article far enough into the future that that link has gone dry, you’re welcome to download the [code straight from GitHub](https://github.com/craiglabenz/dashlander). + +### **Realizing YOUR vision** + +To get started building your dream app or game, head to [antigravity.google](https://antigravity.google) for Google’s Agent Manager and IDE; and to [flutter.dev](https://flutter.dev) to get started with the Flutter SDK. + +Good luck, and we can’t wait to see what you build! \ No newline at end of file diff --git a/sites/www/content/blog/video-hamilton-app-built-in-3-months-with-flutter-reaches-1m-installs/images/0-1m66Jxnec3s8Qnb.webp b/sites/www/content/blog/video-hamilton-app-built-in-3-months-with-flutter-reaches-1m-installs/images/0-1m66Jxnec3s8Qnb.webp new file mode 100644 index 0000000000..e2a37ac271 Binary files /dev/null and b/sites/www/content/blog/video-hamilton-app-built-in-3-months-with-flutter-reaches-1m-installs/images/0-1m66Jxnec3s8Qnb.webp differ diff --git a/sites/www/content/blog/video-hamilton-app-built-in-3-months-with-flutter-reaches-1m-installs/index.md b/sites/www/content/blog/video-hamilton-app-built-in-3-months-with-flutter-reaches-1m-installs/index.md new file mode 100644 index 0000000000..b861236b75 --- /dev/null +++ b/sites/www/content/blog/video-hamilton-app-built-in-3-months-with-flutter-reaches-1m-installs/index.md @@ -0,0 +1,19 @@ +--- +title: "[Video] Hamilton app built in 3 months with Flutter reaches 1M+ installs" +description: "Hamilton and Posse, a design and development agency in New York, had three short months to develop and launch mobile apps for the hit…" +publishDate: 2018-03-26 +author: aguinis +category: spotlight +layout: blog +--- + +<YoutubeEmbed id="kfd-oLypqFI" title="Hamilton iOS and Android app built and featured with Flutter (Flutter Developer Story)" fullwidth="true"/> + + +[Hamilton](https://hamiltonmusical.com/) and [Posse](https://goposse.com/), a design and development agency in New York, had *three* *short months* to develop and launch mobile apps for the hit Broadway show. How did they accomplish that? Using [Flutter](https://flutter.io/?utm_source=youtube&utm_medium=hamilton_flutter_vid), Google’s new mobile UI framework. + +Reaching millions of users — with an outstanding *half a million* monthly active users and featured on both the [App Store](https://itunes.apple.com/us/app/hamilton-the-official-app/id1255231054?mt=8) and [Google Play](https://play.google.com/store/apps/details?id=com.hamilton.app&hl=en) — the apps let fans enter the ticket lottery, buy merchandise, play trivia, take selfies with a #HamCam, read frequently updated news and interviews, and more. + +[Watch this video case study](https://www.youtube.com/watch?v=kfd-oLypqFI&feature=youtu.be) to see how Flutter continues to help apps like Hamilton succeed on iOS and Android. You can read more details about the development of this app on [Posse’s blog post](https://medium.com/@goposse/1d9cd76f95f1). + +Flutter is free and open source. Get started today at [flutter.io](https://flutter.io/?utm_source=youtube&utm_medium=hamilton_flutter_vid). We can’t wait to see what you build! \ No newline at end of file diff --git a/sites/www/content/blog/video-web-ad-support-in-flutter/images/0wz_WITqUIrY1dR7w.webp b/sites/www/content/blog/video-web-ad-support-in-flutter/images/0wz_WITqUIrY1dR7w.webp new file mode 100644 index 0000000000..263aff8f36 Binary files /dev/null and b/sites/www/content/blog/video-web-ad-support-in-flutter/images/0wz_WITqUIrY1dR7w.webp differ diff --git a/sites/www/content/blog/video-web-ad-support-in-flutter/images/0xsAbaMexfbR30wgK.webp b/sites/www/content/blog/video-web-ad-support-in-flutter/images/0xsAbaMexfbR30wgK.webp new file mode 100644 index 0000000000..f6e4d38305 Binary files /dev/null and b/sites/www/content/blog/video-web-ad-support-in-flutter/images/0xsAbaMexfbR30wgK.webp differ diff --git a/sites/www/content/blog/video-web-ad-support-in-flutter/images/1W-CAR3hmZRMA03b20508iw.webp b/sites/www/content/blog/video-web-ad-support-in-flutter/images/1W-CAR3hmZRMA03b20508iw.webp new file mode 100644 index 0000000000..95d9f7cc13 Binary files /dev/null and b/sites/www/content/blog/video-web-ad-support-in-flutter/images/1W-CAR3hmZRMA03b20508iw.webp differ diff --git a/sites/www/content/blog/video-web-ad-support-in-flutter/index.md b/sites/www/content/blog/video-web-ad-support-in-flutter/index.md new file mode 100644 index 0000000000..1b744d6fdd --- /dev/null +++ b/sites/www/content/blog/video-web-ad-support-in-flutter/index.md @@ -0,0 +1,81 @@ +--- +title: "Video & web ad support in Flutter" +description: "Coauthors: Zoey Fan, Harold Benson, Alak Jhunjhunwala" +publishDate: 2024-12-17 +author: zoeyfan +image: images/0xsAbaMexfbR30wgK.webp +category: announcements +layout: blog +--- + +Monetizing your Flutter app just got easier and more rewarding. Today, we are thrilled to announce a new suite of tools and feature updates that provide developers with a range of options to monetize your apps and grow your business. + +### Video ads now available for Flutter developers + +<DashImage figure src="images/0xsAbaMexfbR30wgK.webp" alt="An animated gif showing a sample video ads" caption="An animated gif showing a sample video ads" /> + + +Let’s start by turning your video content into a revenue-generating powerhouse! + +We’d like to announce the launch of the[ Interactive Media Ads (IMA) plugin for Flutter](https://youtu.be/U8x5n6RwZOo). You can now seamlessly integrate high-quality video ads into your Flutter apps on iOS and Android. This means that you can now easily monetize your video content with video ads before (pre-roll), during (mid-roll), or after (post-roll) your video content playback. ******Just like the Google Mobile Ads plugin simplifies the display of mobile ads in your Flutter apps, the [IMA plugin](https://pub.dev/packages/interactive_media_ads) makes it easy to incorporate video ads. We’ve taken care of the heavy lifting, so you can focus on what matters most: creating amazing app experiences. + +**Effortless integration, maximum impact** + +* **Client-side ad insertion:** Ads play in a separate video player, ensuring a smooth viewing experience for your users while giving you more control. + +* **Seamless ad handling:** The plugin requests video ads and handles the playback. It also ensures UI elements such as skip buttons are rendered and ad measurement signals are fired. + +* **Industry standard compliance**: IMA leverages the industry-standard Video Ad Serving Template (VAST) specification, ensuring broad compatibility with ad servers. + +* **Access to premium ad demand**: IMA ensures you can monetize your video content with high quality and premium video ad demand from Google. + +**Get started today!** +Are you ready to supercharge your Flutter app with video ads? Head over to[ pub.dev/packages/interactive_media_ads](https://pub.dev/packages/interactive_media_ads) to explore the IMA plugin and access detailed documentation. You’ll find everything you need to get started, including code samples and step-by-step guides. We’re excited to see how you use the IMA plugin to monetize your Flutter video content and create even more engaging app experiences! + +### Beta: Monetize your Flutter web games with AdSense ads + +<DashImage figure src="images/0wz_WITqUIrY1dR7w.webp" alt="An animated gif showing a sample AdSense Ads in a game" caption="An animated gif showing a sample AdSense Ads in a game" /> + + +Many Flutter developers want a way to monetize their Flutter web projects just like their mobile counterparts. We have been busy working behind the scenes, and have some exciting progress to share! + +Today, we’re thrilled to announce the **beta program** for AdSense for the **H5 games** plugin! With this plugin, you can easily add AdSense [H5 Game Ads](https://adsense.google.com/start/h5-games-ads/) to your web-based (H5) games built in Flutter, opening up new opportunities to generate revenue. + +**What the beta program offers:** + +* Be among the first to integrate AdSense: Get early access to the Flutter AdSense plugin and start experimenting with different ad formats. + +* Shape the future of the plugin: Provide valuable feedback and help us tailor the plugin to the specific needs of Flutter web developers. + +This program is a major step towards a complete ad monetization solution for Flutter web. Currently, this Beta program focuses on enabling AdSense for **Flutter web games**. We’re collaborating with the AdSense team to develop solutions for Flutter web apps in the future. + +If you are a Flutter web **game** developer, and interested in participating, please fill out the [H5 AdSense application form](https://adsense.google.com/start/h5-beta/?src=flutter). Once you are approved into the beta program, use [pub.dev/packages/google_adsense](https://pub.dev/packages/google_adsense) to get started! + +### Higher conversion and better user experience with Deep Linking + +Adding ads is one of the ways to become a successful business. Sometimes your business requires selling goods and services in your app. You have probably already spent an enormous amount of time and resources in creating SMS/email marketing and Ads campaigns, and hoping these campaigns will drive effective conversion. But what if you could take your campaigns to the next level by directing customers from a marketing campaign straight to your app and the specific item they’re interested in? + +With [deep linking](https://docs.flutter.dev/ui/navigation/deep-linking) in Flutter, you can do just that. When someone clicks a link in your campaign, they’ll be taken directly to your app and the product or service you’re promoting. This seamless transition increases the **chances of conversion by 2.3x** compared to linking to your website. + +Here’s why deep linking is a game-changer for advertisers: + +* **Drive higher conversions:** By bringing customers directly to your app, you eliminate distractions and make it easier for them to complete their purchase. + +* **Personalize the experience:** Deep linking allows you to create personalized experiences for each customer. When they click on a link, they’ll be taken to the exact product or service they’re interested in. + +* **Track campaign performance:** With deep linking, you can track the effectiveness of your campaigns and see how many people are clicking through to your app. This data can help you optimize your campaigns for better results. + +While deeplinking offers all these great benefits, it can be quite cumbersome to set up. Therefore, we’re also making deep linking more robust and reliable than ever before. With full [web and app check validation support](https://docs.flutter.dev/tools/devtools/deep-links) across iOS and Android in Flutter DevTools, you can ensure your deep links are configured correctly and securely. Your users will enjoy seamless transitions between web and app content, increasing engagement and satisfaction! To start, go to Flutter DevTools, and open the Deeplinking validation tab. + +<DashImage figure src="images/1W-CAR3hmZRMA03b20508iw.webp" alt="An animated gif showing Deeplinking Validation in Flutter DevTools" caption="An animated gif showing Deeplinking Validation in Flutter DevTools" /> + + +We’re committed to providing you, the Flutter developer, with the tools and resources you need to succeed. These new features are a testament to that commitment. So go ahead, take your Flutter app to new heights, and unlock its full potential! + +**Explore More:** + +* Dive into the world of Flutter monetization:[ flutter.dev/monetization](https://flutter.dev/monetization) + +* Master the art of deep linking:[ Flutter documentation](https://docs.flutter.dev/development/ui/navigation/deep-linking) + +* Stay updated with the latest Flutter news:[Flutter blog](https://medium.com/flutter) \ No newline at end of file diff --git a/sites/www/content/blog/we-hear-you-learnings-from-q3-2019-flutter-user-survey/images/0UMfWbaBispXV0tMD.webp b/sites/www/content/blog/we-hear-you-learnings-from-q3-2019-flutter-user-survey/images/0UMfWbaBispXV0tMD.webp new file mode 100644 index 0000000000..780c69529a Binary files /dev/null and b/sites/www/content/blog/we-hear-you-learnings-from-q3-2019-flutter-user-survey/images/0UMfWbaBispXV0tMD.webp differ diff --git a/sites/www/content/blog/we-hear-you-learnings-from-q3-2019-flutter-user-survey/images/0c_10W3HCaw0k6u_h.webp b/sites/www/content/blog/we-hear-you-learnings-from-q3-2019-flutter-user-survey/images/0c_10W3HCaw0k6u_h.webp new file mode 100644 index 0000000000..eaaf64d050 Binary files /dev/null and b/sites/www/content/blog/we-hear-you-learnings-from-q3-2019-flutter-user-survey/images/0c_10W3HCaw0k6u_h.webp differ diff --git a/sites/www/content/blog/we-hear-you-learnings-from-q3-2019-flutter-user-survey/images/0jKeTRjxJ0q0gFu2H.webp b/sites/www/content/blog/we-hear-you-learnings-from-q3-2019-flutter-user-survey/images/0jKeTRjxJ0q0gFu2H.webp new file mode 100644 index 0000000000..30f12cea98 Binary files /dev/null and b/sites/www/content/blog/we-hear-you-learnings-from-q3-2019-flutter-user-survey/images/0jKeTRjxJ0q0gFu2H.webp differ diff --git a/sites/www/content/blog/we-hear-you-learnings-from-q3-2019-flutter-user-survey/images/0rHPbdMZoe86uwe1H.webp b/sites/www/content/blog/we-hear-you-learnings-from-q3-2019-flutter-user-survey/images/0rHPbdMZoe86uwe1H.webp new file mode 100644 index 0000000000..c726874180 Binary files /dev/null and b/sites/www/content/blog/we-hear-you-learnings-from-q3-2019-flutter-user-survey/images/0rHPbdMZoe86uwe1H.webp differ diff --git a/sites/www/content/blog/we-hear-you-learnings-from-q3-2019-flutter-user-survey/images/1kC6Z0A82B9Yk4mV88fo9CQ.webp b/sites/www/content/blog/we-hear-you-learnings-from-q3-2019-flutter-user-survey/images/1kC6Z0A82B9Yk4mV88fo9CQ.webp new file mode 100644 index 0000000000..c69aafc3bf Binary files /dev/null and b/sites/www/content/blog/we-hear-you-learnings-from-q3-2019-flutter-user-survey/images/1kC6Z0A82B9Yk4mV88fo9CQ.webp differ diff --git a/sites/www/content/blog/we-hear-you-learnings-from-q3-2019-flutter-user-survey/index.md b/sites/www/content/blog/we-hear-you-learnings-from-q3-2019-flutter-user-survey/index.md new file mode 100644 index 0000000000..870393c2a5 --- /dev/null +++ b/sites/www/content/blog/we-hear-you-learnings-from-q3-2019-flutter-user-survey/index.md @@ -0,0 +1,82 @@ +--- +title: "We hear you — learnings from Q3 2019 Flutter user survey" +description: "On performance, add-to-app, plugins, and more" +publishDate: 2019-11-19 +author: jayoung-lee +image: images/0UMfWbaBispXV0tMD.webp +category: announcements +layout: blog +--- + +## We hear you — learnings from Q3 2019 Flutter user survey + +<DashImage figure src="images/1kC6Z0A82B9Yk4mV88fo9CQ.webp" /> + + +Flutter’s User Experience team runs a survey every quarter to understand what we can do to help you be successful with Flutter. Over 7,500 users participated in the Q3 survey, and we’re here to share what we learned, [as always](https://medium.com/flutter/search?q=survey). + +Overall, 92% of the users were either *somewhat satisfied* or *very satisfied* with Flutter. This is the first question we ask in every survey, because it is important for us to build a product that you love. We are excited to see the same level of satisfaction as [last quarter](https://medium.com/flutter/what-do-flutter-package-users-need-6ecba57ed1d6). + +## **Performance** + +Flutter continues to polish its foundation, and that includes improving the experience around Flutter’s performance. To better understand how we’re doing on this topic, we covered the performance of Flutter as one of the main topics in this survey. + +While 78% of the users had experience with release or profile mode (performance should only be assessed in release or profile mode), 85% of them were positively satisfied (either *somewhat satisfied* or *very satisfied*) with Flutter’s performance. In addition, we asked about sub-categories of performance: rendering performance, memory performance, power usage performance, and binary size. We learned that 10% to 20% of users don’t know how to assess these aspects of performance, and the least satisfying aspect was the binary size (54% positively satisfied). For those who need more information about how to assess and optimize different aspects of performance, we will soon publish how-to guides, in addition to [the existing documentation on performance](https://flutter.dev/docs/perf). The survey was an opportunity for us to understand that some performance metrics, such as power usage or memory performance, are harder to assess than others. + +<DashImage figure src="images/0UMfWbaBispXV0tMD.webp" alt="*10% to 20% of users don’t know how to assess these performances*" caption="*10% to 20% of users don’t know how to assess these performances*" /> + + +We also learned that only about half of the users (47%) are using [Dart DevTools](https://flutter.dev/docs/development/tools/devtools/overview) to measure the performance of their Flutter app, which is a suite of performance tools for Dart and Flutter. Half of those who haven’t used Dart DevTools said that they didn’t know they exist (48%) and a fifth of them didn’t know how to use them (21%). We will put more effort on increasing the awareness of the DevTools. We will also more tightly integrate DevTools with the IDEs, making it easier to launch specific DevTools functionality, potentially without requiring opening a separate web browser. + +<DashImage figure src="images/0jKeTRjxJ0q0gFu2H.webp" alt="*Performance profiling tool preference*" caption="*Performance profiling tool preference*" /> + + +## **Add-to-app** + +Add-to-app is the incremental adoption of Flutter by adding a Flutter-rendered screen into an existing Android or iOS app, rather than creating a from-scratch Flutter-only project. This is a great way to adopt Flutter when there are constraints that prevent you from writing an app from scratch, and the team is investing into supporting such a journey. + +From the survey, we learned that 56% of respondents are interested in add-to-app, and 36% of them have already attempted implementing add-to-app in their existing apps, while 40% were not aware that it’s currently possible with Flutter. Experimental add-to-app instructions are described in the [wiki page](https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps). Although many users wanted to try add-to-app rather than building a full Flutter app, 64% of the interested users said that they would attempt to transition to a full Flutter app in a year, instead of remaining in a hybrid app. + +The survey also helped the team identify future plans, such as which UI pattern to prioritize (i.e., full screen vs. partial screen), which tooling to improve first, and so on. It was also news to us that the lack of coverage in middleware (such as platform ⇄ Flutter interoperability) was the biggest challenge to those who tried add-to-app already, even more than the lack of capability/APIs or documentation. The team is working towards a better add-to-app experience. You can watch their progress in the [Add to App 2019 GitHub project](https://github.com/flutter/flutter/projects/51). + +## **Plugins** + +Ecosystem is another crucial part of Flutter; this enables developers to extend the capabilities of Flutter with packages and plugins when the core framework doesn’t support a particular feature. In the Q2 survey, we covered a number of [ecosystem-related questions](https://medium.com/flutter/what-do-flutter-package-users-need-6ecba57ed1d6) to understand your pain points. In Q3, we added two open-ended questions to better understand which plugins (or lack of plugins) are impeding the development and release of your apps. + +16% of all respondents answered these questions, and we identified over 70 common themes. Some of the frequently-mentioned themes are listed below. The main takeaway for us is that the listed plugins already exist on pub.dev. This tells us that raising the quality of existing plugins should be one of the most important goals in the coming quarters. + +1. Geo (e.g., Google Maps) + +1. Media (e.g., audio/video player, audio/video recording) + +1. Webview + +1. Ads (e.g., Admob, native ads) + +1. Payment + +1. Data management + +1. Image support (e.g. Lottie, SVG) + +1. Camera + +1. Notifications + +1. Background services + +We will focus on improving your experience around Flutter’s ecosystem and also make it easier for you to find quality plugins. As a starting point, Dart has recently released a “[verified publishers](https://medium.com/dartlang/verified-publishers-98f05466558a)” feature for [pub.dev](http://pub.dev) (on Dart’s 8th birthday!) to make it easy for users to identify trustworthy publishers. This was one of the planned improvements mentioned in the recap of the [previous survey](https://medium.com/flutter/what-do-flutter-package-users-need-6ecba57ed1d6). Please keep your eye out for further improvements. + +<DashImage figure src="images/0rHPbdMZoe86uwe1H.webp" alt="*First page of dart.dev features some verified Flutter packages*" caption="*First page of dart.dev features some verified Flutter packages*" /> + + +## **Frustrating experiences** + +We also asked another open-ended question about recurring annoyances. Although [GitHub issues](https://github.com/flutter/flutter/issues) are open to anyone who wants to file, comment, or upvote, on this survey we wanted to capture frictions that users might not have reported on Github. + +We collected over 1,600 comments from this one question, and several team members dedicated many hours to reading and sorting them into categories. We were able to capture some minor but frequent failures that may potentially lead to annoyance and frustration, such as issues related to hot reload and debugging, issues with upgrades and migration, and so on. The sub-teams of Flutter were notified of issues relevant to their work, and it led to many productive discussions. For example, we are working on [better reporting](https://github.com/flutter/flutter/issues/42549) to understand hot reload issues. If you have recurring annoyances, do not hesitate to file an issue. We are tracking those with the “a: annoyance” [label](https://github.com/flutter/flutter/labels/a%3A%20annoyance). + +<DashImage figure src="images/0c_10W3HCaw0k6u_h.webp" alt="*Track the progress on Github*" caption="*Track the progress on Github*" /> + + +Thank you again for participating in the survey! Please continue to provide valuable feedback to the Flutter team and help us plan for the coming quarters. Flutter’s UX research team also performs a variety of user experience studies so that we can learn how to make your experience with Flutter more pleasant. If you are interested in participating, please [sign up](http://flutter.dev/research-signup) for future studies. \ No newline at end of file diff --git a/sites/www/content/blog/we-rebuilt-flutters-websites-with-dart-and-jaspr/images/1y6lc2WLpxXBKsuoKJywapQ.webp b/sites/www/content/blog/we-rebuilt-flutters-websites-with-dart-and-jaspr/images/1y6lc2WLpxXBKsuoKJywapQ.webp new file mode 100644 index 0000000000..dacb9de8cb Binary files /dev/null and b/sites/www/content/blog/we-rebuilt-flutters-websites-with-dart-and-jaspr/images/1y6lc2WLpxXBKsuoKJywapQ.webp differ diff --git a/sites/www/content/blog/we-rebuilt-flutters-websites-with-dart-and-jaspr/index.md b/sites/www/content/blog/we-rebuilt-flutters-websites-with-dart-and-jaspr/index.md new file mode 100644 index 0000000000..9cb36f4a50 --- /dev/null +++ b/sites/www/content/blog/we-rebuilt-flutters-websites-with-dart-and-jaspr/index.md @@ -0,0 +1,137 @@ +--- +title: "We rebuilt Flutter’s websites with Dart and Jaspr" +description: "Despite Dart starting out as a web language and being used every day to build apps across platforms, including the web, our own websites…" +publishDate: 2026-04-15 +author: parlough +image: images/1y6lc2WLpxXBKsuoKJywapQ.webp +layout: blog +--- + +<DashImage figure src="images/1y6lc2WLpxXBKsuoKJywapQ.webp" alt="Rebuilding three websites using Jaspr, a Dart-based, open-source web framework." caption="Rebuilding three websites using Jaspr, a Dart-based, open-source web framework." /> + + +Despite Dart starting out as a web language and being used every day to build apps across platforms, including the web, our own websites ([dart.dev](https://dart.dev), [flutter.dev](https://flutter.dev), [docs.flutter.dev](https://docs.flutter.dev)) relied on a fragmented mix of non-Dart tools. That’s finally changed. We’ve migrated all three websites to use [Jaspr](https://jaspr.site/), an open-source framework for building websites with Dart. + +The result is a unified stack with a consistent developer experience where contributing only requires Dart. If you’re curious about building web experiences with Dart beyond standard Flutter web apps, this post explores what motivated our migration and how Dart and Jaspr made it all possible. + +## A fragmented and unfamiliar technical stack + +While the previous setup of our sites worked, their implementations were fragmented, and required increasingly more effort to update the sites to meet our evolving needs. The documentation sites were built with [Eleventy](https://www.11ty.dev/), a Node.js static-site generator. Meanwhile, flutter.dev had a completely separate setup, powered by [Wagtail](https://wagtail.org/), a CMS built on Python and Django. + +This fragmentation meant that anyone wanting to contribute to or maintain our sites needed additional experience and tooling outside the Dart ecosystem: Node.js tooling for one set of sites, Python for another. While some surrounding infrastructure and interactive components were already built with Dart, the separate ecosystems limited code sharing, significantly increased set-up and contribution friction, and grew increasingly complicated. + +We wanted to change that. We wanted a single, unified stack built on the language and tools our team and community already know. We also had growing ambitions and needs for interactivity on our sites, from richer code samples to quizzes for tutorials. Our existing setups made each new interactive element an uphill battle, often requiring one-off imperative DOM logic. + +## Finding a unified solution in Jaspr + +Jaspr is a versatile Dart web framework that supports client-side rendering, server-side rendering, and static site generation. Beyond being a traditional DOM-based (with HTML and CSS) web framework and being written in the language we already know, Jaspr stood out for a few reasons: + +**Flutter skills transfer directly.** The Jaspr framework and its component model were designed to feel natural and familiar to any Flutter developer while being compatible with the DOM model of the web. If you’ve written a Flutter widget before, you can read this: + +```dart +class FeatureCard extends StatelessComponent { + const FeatureCard({ + required this.title, + required this.description, + super.key, + }); + + final String title; + final String description; + + @override + Component build(BuildContext context) { + return div(classes: 'feature-card', [ + h3([.text(title)]), + p([.text(description)]), + ]); + } +} +``` + + +With Jaspr, contributors can directly apply the Dart and Flutter experience they already have to a new platform, significantly lowering the barrier to entry for team and community members who want to improve our documentation and websites. + +**Seamless support for partial hydration.** One major underlying reason for this exploration and migration was to make it easier to build and integrate interactive experiences on our sites. Jaspr’s built-in support for partial hydration allows each page to be prerendered as static HTML, then the client-side logic is attached only for the components that need it. This is perfect for websites like ours, where the majority of the content is static and only small pockets of interactivity are needed, ensuring quick page loading and good SEO. + +**Jaspr Content for Markdown-driven sites.** Jaspr also provides [Jaspr Content](https://docs.jaspr.site/content), a package that supports quickly building content-driven sites. It provides enough out-of-the-box functionality to create a running Markdown-based website in just a few minutes while also being easy to expand and customize extensively. This built-in functionality saved a significant amount of time while the customizability enabled us to keep our original functionality and content practices intact. + +## What we gained + +The migration brought all the benefits we imagined and more, both for the sites themselves and the contribution experience. + +**A singular, unified toolchain.** With everything written in Dart, not only do you need just one SDK to contribute, we also gained access to Dart’s powerful, unified tooling. We can manage all dependencies with `dart pub`, format code with `dart format`, analyze it with `dart analyze`, and then test it with `dart test`. Managing the site now requires only one set of tools to know, one set of conventions to follow, and one ecosystem to stay current with, and it’s the one we’re already most familiar with. + +**A stack our contributors already know.** Our websites have a lot of contributors, from engineers, to technical writers, to passionate community members. We want everyone to be able to contribute, but the fragmented setup was complex and unfamiliar to most. Now the sites are implemented as standard Dart projects, and if you know Dart, you have everything you need. We hope this lowers the barrier for team and community members who want to help improve Flutter and Dart’s documentation. + +**Less had to change than you’d expect.** With Jaspr Content supporting most of what we needed out of the box, such as templating support, Markdown, and data loading, our content and writing workflows barely needed to change. Nor did our styles, as we already used [Sass](https://sass-lang.com/), a CSS extension language, which is actually implemented in Dart, and therefore requires an even simpler setup than we had before. + +## The collaborative migration + +Overall, the site migration to Jaspr and Jaspr Content went well, but there were, of course, some challenges along the way. We occasionally ran into issues as well as opportunities for improvement with both Dart’s web tooling and Jaspr itself. + +What made the migration possible was [Kilian](https://github.com/schultek), Jaspr’s creator and maintainer. Beyond creating Jaspr, he supported us throughout the migration. He migrated components as early proofs of concept, responded to issues, shipped fixes, improved the developer experience, and even built out Jaspr Content with our websites as a driving use case. To support this ongoing effort and formalize the collaboration, we partnered with Kilian and his consultancy, [Netlight](https://www.netlight.com/), to help us migrate the rest of our web presence and continue investing directly in Jaspr. It was a genuinely collaborative process. Our sites and Jaspr both grew as a result. + +In the Dart and Flutter ecosystem, **the community is everything** and what Kilian has provided to the community with Jaspr is a great example of that. Jaspr has shown itself to be a powerful and modern web framework that is well maintained, responsive to feedback, and ready for you to try out. Thank you, Kilian! + +To hear Kilian’s perspective on building and maintaining the framework, check out his article: Jaspr: [Why web development in Dart might just be a good idea](https://opensource.googleblog.com/2026/04/jaspr-why-web-development-in-dart-might-just-be-a-good-idea.html). + +## Dart and Jaspr growing together + +One of the most rewarding aspects of building on an all-Dart stack is that improvements to the Dart language and surrounding tooling benefit everything. Not just your Flutter apps, but your websites too. Here are a few recent Dart features that have directly impacted and improved the experience of building with Jaspr. + +**Dot shorthands make component trees cleaner.** Dart 3.10 introduced support for a [dot shorthand syntax](https://dart.dev/language/dot-shorthands) enabling you to omit the type name from static member accesses when they can be inferred from the context. Kilian took advantage of this by consolidating several component constructors onto the `Component` class and designing them to work naturally with the new syntax: + +```dart +Component build(BuildContext context) => const div([ + // After the API changes: + h1([Component.text('Dash says hi!')]), + Component.fragment([ + Component.text('First element'), + Component.text('Second element'), + ]), + Component.empty(), + + // With dot shorthands: + h1([.text('Dash says hi!')]), + .fragment([ + .text('First element'), + .text('Second element'), + ]), + .empty(), +]); +``` + + +The result was a more consistent API with better discoverability and a concise syntax that still works in constant contexts. Best of all, Jaspr’s CLI comes with a `jaspr migrate` command that automatically handled the migration to the new API as well as other changes. + +**Null-aware collection elements simplify conditional rendering.** Dart 3.8 added support for [null-aware collection elements](https://dart.dev/language/collections#null-aware-element), providing a clean syntax to conditionally include non-null values in collections. In Jaspr code, where you’re regularly composing lists of child components, they offer an elegant way to handle conditional UI elements: + +```dart +Component build(BuildContext context) => div(classes: 'header', [ + h1([.text('Welcome to Flutter!')]), + + // Before null-aware collection elements: + if (eventBanner != null) eventBanner!, + + // With a null-aware collection element: + ?eventBanner, +]); +``` + + +No more verbose `if` checks and not-null assertions cluttering your component trees. + +**Modern, lightweight JS interop and compilation to WebAssembly.** To enable efficient access to modern web APIs and compilation to WebAssembly, Dart 3.3 introduced new [JS interop](https://dart.dev/interop/js-interop) libraries as well as `package:web`. Jaspr was quick to migrate to and support the new APIs, ensuring Jaspr developers could benefit from their new capabilities and build modern Dart apps. Building on this, Jaspr additionally supports experimental compilation to WebAssembly when running on the client. In fact, dart.dev already uses and benefits from this support on compatible browsers. + +**A helpful, integrated analyzer plugin.** For a while, Jaspr had a helpful linting package built on top of `package:custom_lint`, helping developers write idiomatic and correct Jaspr code. With the release of official [analyzer plugin](https://dart.dev/tools/analyzer-plugins) support in Dart 3.10, Jaspr migrated to adopt the feature. The plugin provides a great example of what is possible, providing Jaspr-specific diagnostics and code assists. For example, it can convert between component types or quickly wrap a component with another, similar to the assists you might already be used to with Flutter. + +None of these features were built specifically for Jaspr. They’re improvements to the Dart language and tooling that benefit the entire ecosystem, not just Flutter. For some of them, Jaspr was able to immediately take advantage, while others required framework changes from Kilian and contributors to unlock their potential. Either way, it’s clear that Dart keeps evolving and that evolution continues to open up improvements and possibilities for everything built with it, including Jaspr and Flutter. + +## What’s next and how to get started + +We’re not done yet. Now that our websites share this new technical stack, we can start to share more code, build new interactive features, and continue to improve Dart’s web development story. We’re also migrating the Dart and Flutter blogs from Medium to being directly hosted on our Jaspr-powered sites. You’ll hopefully be able to read this very post there soon. + +If you’re a Dart or Flutter developer curious about building websites with the skills you already have, there’s never been a better time to try. Jaspr is a great option for content-heavy sites, such as landing pages and documentation. It can even naturally [integrate with your Flutter web apps](https://docs.jaspr.site/going_further/flutter_embedding). Try it out now on Jaspr’s [online playground](https://playground.jaspr.site/) (which is also built with Jaspr!) or by following the [Jaspr quickstart](https://docs.jaspr.site/quick_start). + +Or, if you’re interested in contributing to the [Flutter](https://github.com/flutter/website) or [Dart](https://github.com/dart-lang/site-www) documentation sites, the barrier to entry just got a lot lower. Now with Jaspr, **all you need is Dart**. \ No newline at end of file diff --git a/sites/www/content/blog/web-support-for-flutter-goes-beta/images/05vkz66kMYk5DTfSS.webp b/sites/www/content/blog/web-support-for-flutter-goes-beta/images/05vkz66kMYk5DTfSS.webp new file mode 100644 index 0000000000..32c8bd272a Binary files /dev/null and b/sites/www/content/blog/web-support-for-flutter-goes-beta/images/05vkz66kMYk5DTfSS.webp differ diff --git a/sites/www/content/blog/web-support-for-flutter-goes-beta/images/0E9vaDY0XLc4dCR2J.webp b/sites/www/content/blog/web-support-for-flutter-goes-beta/images/0E9vaDY0XLc4dCR2J.webp new file mode 100644 index 0000000000..f6160adaa4 Binary files /dev/null and b/sites/www/content/blog/web-support-for-flutter-goes-beta/images/0E9vaDY0XLc4dCR2J.webp differ diff --git a/sites/www/content/blog/web-support-for-flutter-goes-beta/images/0M7sTluUPVsk6DcnU.webp b/sites/www/content/blog/web-support-for-flutter-goes-beta/images/0M7sTluUPVsk6DcnU.webp new file mode 100644 index 0000000000..86eac5e213 Binary files /dev/null and b/sites/www/content/blog/web-support-for-flutter-goes-beta/images/0M7sTluUPVsk6DcnU.webp differ diff --git a/sites/www/content/blog/web-support-for-flutter-goes-beta/images/0QApzX3FOYgAGCFRK.webp b/sites/www/content/blog/web-support-for-flutter-goes-beta/images/0QApzX3FOYgAGCFRK.webp new file mode 100644 index 0000000000..c7b2aa9a22 Binary files /dev/null and b/sites/www/content/blog/web-support-for-flutter-goes-beta/images/0QApzX3FOYgAGCFRK.webp differ diff --git a/sites/www/content/blog/web-support-for-flutter-goes-beta/index.md b/sites/www/content/blog/web-support-for-flutter-goes-beta/index.md new file mode 100644 index 0000000000..91d1bf963e --- /dev/null +++ b/sites/www/content/blog/web-support-for-flutter-goes-beta/index.md @@ -0,0 +1,111 @@ +--- +title: "Web support for Flutter goes beta" +description: "Posted by Mariam Hasnany, Product Manager, Flutter" +publishDate: 2019-12-11 +author: mariam_hasnany +image: images/0M7sTluUPVsk6DcnU.webp +category: announcements +layout: blog +--- + +We’re pleased to announce that web support for Flutter is now in beta! + +## Why did we bring Flutter to the web? + +Developers build apps that need to run both on mobile and web. It’s important to us that you can design and build what you want, and know that with Flutter it will work beautifully anywhere you need. As a developer, learning a single set of skills that can easily transfer across multiple platforms is desirable. Web support for Flutter enables developers to use the same code, ship features faster, and ensure consistency for their experiences across devices. Additionally, a powerful Dart compiler for the web and a Flutter architecture that is designed with portability in mind makes it easier to create delightful interactive web experiences using Flutter. + +## More than a preview + +Since releasing web support as a [tech preview](https://medium.com/flutter/bringing-flutter-to-the-web-904de05f0df0) at Google I/O this year, and the start of the [early adopter program](https://medium.com/flutter/flutter-for-web-early-adopter-program-now-open-9f1fb146e4c4) in July, we’ve been working hard to support the growing interest in extending Flutter’s web support both at Google and in the wider public. + +### So, what does beta for web mean? + +With the Flutter 1.12 release, Flutter’s web support graduates from technical preview to beta. When you’re on the beta channel and have enabled web support, creating a new Flutter project not only includes Android and iOS host apps, but now also includes a web/ directory that contains everything you need to compile and run the same project code in a browser. + +We believe Flutter’s web support is starting to stabilize and is ready for more adventurous developers to start using for a number of scenarios. As we move into this next stage of development, we will continue to make changes and improve accessibility, test coverage, and more. + +## Scenarios to try + +As we’ve been developing Flutter’s support for running on the web, we have particularly focused on a number of scenarios that we think are well suited for the characteristics of Flutter. We believe that our feature set is complete enough to enable developers to build rich, interactive web experiences. While working with our early adopter partners, we have been validating and refining support for the following scenarios. + +**Connected, standalone app** + +Flutter can enable developers to build a single app from the same code across both mobile and browser experiences. [Journey](https://startyourjourney.io/), one of our early adopters, used Flutter to build an app across multiple platforms. + +<DashImage figure src="images/0E9vaDY0XLc4dCR2J.webp" alt="Journey, a social app, recently launched a cross-platform app using Flutter" caption="Journey, a social app, recently launched a cross-platform app using Flutter" /> + + +Luke O’Brien, Founder of Journey, states “*Four months ago I was about to build Journey for Android-only for the MVP. I discovered Flutter and thought, ‘This is too good to be true’, but decided to run with it. It has been the best decision I have made to date. Flutter has cut development time in half (probably more than half) and we’ve now launched across Android, iOS, and web — doubling user-growth potential. It’s difficult to overstate the impact Flutter has had in turning my vision into reality.’* + +**Embedded interactive content** + +One scenario is embedding a rich, data-centric, mini app within a parent site; there is no need for navigation services or other app-like functionality. Embedding a new car configurator, a crossword puzzle, or interactive data visualizations into an existing website are just a few key examples that fit this scenario. Early adopter [AEI Studio’s showcase](https://studio.aei.dev/showcase/) of chatbots embedded Flutter within their web chat dialog showcasing animations, text input by keyboard, and more. + +<DashImage figure src="images/0M7sTluUPVsk6DcnU.webp" alt="Weatherbot is one of AEI Studio’s chatbots that embed Flutter within their web chat dialog" caption="Weatherbot is one of AEI Studio’s chatbots that embed Flutter within their web chat dialog" /> + + +**Lite apps** + +Even though Flutter’s custom mobile runtime is still able to provide a smoother experience today, sometimes the friction of app installation impedes users from getting started. An existing Flutter app that has a lightweight web experience gives companies the best of both worlds. Although the primary consumption of the app would be on mobile, a lite web app could provide a less feature rich experience with related functionality using the same tools, frameworks, UI components, and business logic. + +**Companion apps** + +A companion app is a web experience built using Flutter to support your primary consumption mobile app. For example, using Flutter to build a web app that enables admins or internal users to create content or manage the backend for your existing Flutter mobile app. Although this web app is considered a separate experience, it can leverage much of the same code from the mobile app. + +## Plugins are here! + +Flutter has a concept of plugins, which allows you to talk to native libraries for the platform you are running on. When running your Flutter app on the web, you can get full access to existing JS libraries. We do all the JS-interop code behind the scenes so the plugin works as you’d expect on both mobile and web. We’ve already implemented a handful of the top-requested plugins so they work consistently across your native and web applications. Now, you can also [write your own plugins](https://medium.com/flutter/how-to-write-a-flutter-web-plugin-5e26c689ea1) as [Ben Hagan](https://github.com/cbenhagen) did for video_player, and [Hadrien Lejard](https://github.com/lejard-h) did for the sentry package*.* The following packages are updated: + +* [shared_preferences](https://pub.dev/packages/shared_preferences) + +* [firebase_core](https://pub.dev/packages/firebase_core) + +* [firebase_auth](https://pub.dev/packages/firebase_auth) + +* [google_sign_in](https://pub.dev/packages/google_sign_in) + +* [url_launcher](https://pub.dev/packages/google_sign_in) + +* [video_player](https://pub.dev/packages/video_player) + +* [sentry](https://pub.dev/packages/sentry) + +We have also added new platform tagging and filtering on the pub.dev package repository. + +First, on a package detail page, we list which platforms the package supports. This makes it easy to identify if a package has web support. + +<DashImage figure src="images/05vkz66kMYk5DTfSS.webp" alt="*pub.dev package detail page showing SDK and platform compatibility tags*" caption="*pub.dev package detail page showing SDK and platform compatibility tags*" /> + + +The search UI also has new filters, so you can find packages that have web support. This is based on the new [platform manifest tags](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms) that are now available in Flutter 1.12. + +<DashImage figure src="images/0QApzX3FOYgAGCFRK.webp" alt="*pub.dev search UI showing SDK and platform filter support*" caption="*pub.dev search UI showing SDK and platform filter support*" /> + + +## Road to stable + +We made a lot of progress with beta, but we still have a lot of work to do. Our performance work is not complete and we are working on broadening our scope for accessibility, browser compatibility and more. + +**Accessibility** + +We have accessibility support on mobile browsers through TalkBack on Android and VoiceOver on iOS. Some of the features that are already implemented for assistive technologies across platforms include things like UI traversal and traversal order, UI interaction cues like tappable, labels, editable, incremental, image, live region, and checkable. **And, we’re working on adding screen reader support for desktop web browsers. + +**Browser support** + +As Flutter evolves from a mobile-only framework to also covering desktop ux idioms, Flutter’s support for desktop web browsers will improve and feel more seamless. We plan to support and test for Chrome, Edge, Firefox, and Safari on desktop and mobile browsers. + +**Test coverage** + +Since the preview, we increased our test coverage both on the framework and on the Flutter web engine. As of today, we run automated tests on Chrome, and we manually test Safari. There is still more testing work to do, and regressions may crop up in untested scenarios. + +## Try Flutter’s web support, contribute, and share! + +Now is the perfect time to try Flutter’s web support! Go to flutter.dev/web to get started, and find examples, documentation, and more. If you’ve already experimented with Flutter’s web support, you can [switch to the beta channel](https://github.com/flutter/flutter/wiki/Flutter-build-release-channels). + +There are over 1800 Flutter plugins that exist today; however, most are for iOS or Android. You can help bridge the gap between mobile and web by adding web support to existing plugins or by building your own. To help guide you, we published an article about [how to write a web plugin](https://medium.com/flutter/how-to-write-a-flutter-web-plugin-5e26c689ea1). + +## Closing thoughts + +We hope that you are excited about Flutter’s web support advancing to the beta channel, and feel our commitment as we get closer to a production-quality release of web support. + +We [welcome feedback](https://flutter.io/support/), and hope that you share what you’re working on using #Flutter. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences! \ No newline at end of file diff --git a/sites/www/content/blog/what-are-the-important-difficult-tasks-for-flutter-devs-q1-2020-survey-results/images/0OfKgvG4OS6Jt7Wum.webp b/sites/www/content/blog/what-are-the-important-difficult-tasks-for-flutter-devs-q1-2020-survey-results/images/0OfKgvG4OS6Jt7Wum.webp new file mode 100644 index 0000000000..6c9face34a Binary files /dev/null and b/sites/www/content/blog/what-are-the-important-difficult-tasks-for-flutter-devs-q1-2020-survey-results/images/0OfKgvG4OS6Jt7Wum.webp differ diff --git a/sites/www/content/blog/what-are-the-important-difficult-tasks-for-flutter-devs-q1-2020-survey-results/images/0cqbZUVzw4XSlZyJR.webp b/sites/www/content/blog/what-are-the-important-difficult-tasks-for-flutter-devs-q1-2020-survey-results/images/0cqbZUVzw4XSlZyJR.webp new file mode 100644 index 0000000000..796a6d3651 Binary files /dev/null and b/sites/www/content/blog/what-are-the-important-difficult-tasks-for-flutter-devs-q1-2020-survey-results/images/0cqbZUVzw4XSlZyJR.webp differ diff --git a/sites/www/content/blog/what-are-the-important-difficult-tasks-for-flutter-devs-q1-2020-survey-results/images/0nPtVnwI_F22HmC_F.webp b/sites/www/content/blog/what-are-the-important-difficult-tasks-for-flutter-devs-q1-2020-survey-results/images/0nPtVnwI_F22HmC_F.webp new file mode 100644 index 0000000000..8ce4918e9d Binary files /dev/null and b/sites/www/content/blog/what-are-the-important-difficult-tasks-for-flutter-devs-q1-2020-survey-results/images/0nPtVnwI_F22HmC_F.webp differ diff --git a/sites/www/content/blog/what-are-the-important-difficult-tasks-for-flutter-devs-q1-2020-survey-results/images/0vABPU2mLHiRog8Vw.webp b/sites/www/content/blog/what-are-the-important-difficult-tasks-for-flutter-devs-q1-2020-survey-results/images/0vABPU2mLHiRog8Vw.webp new file mode 100644 index 0000000000..5fe89d8bf6 Binary files /dev/null and b/sites/www/content/blog/what-are-the-important-difficult-tasks-for-flutter-devs-q1-2020-survey-results/images/0vABPU2mLHiRog8Vw.webp differ diff --git a/sites/www/content/blog/what-are-the-important-difficult-tasks-for-flutter-devs-q1-2020-survey-results/index.md b/sites/www/content/blog/what-are-the-important-difficult-tasks-for-flutter-devs-q1-2020-survey-results/index.md new file mode 100644 index 0000000000..7b1cb3da9e --- /dev/null +++ b/sites/www/content/blog/what-are-the-important-difficult-tasks-for-flutter-devs-q1-2020-survey-results/index.md @@ -0,0 +1,85 @@ +--- +title: "What are the important & difficult tasks for Flutter devs? — Q1 2020 survey results" +description: "Written by JaYoung Lee and Youyang Hou" +publishDate: 2020-05-15 +author: jayoung-lee +image: images/0OfKgvG4OS6Jt7Wum.webp +category: announcements +layout: blog +--- + +## What are the important & difficult tasks for Flutter devs? — Q1 2020 survey results + +Every quarter, we survey Flutter developers to better understand how you’re using Flutter and what we can improve. Today we’re posting the results of the eighth quarterly survey, which took place in February 2020. We received about 6,000 responses over 8 days and were happy to learn that an all-time high of 94.5% of developers report positive satisfaction with Flutter. While we ask some questions every quarter to understand longitudinal trends, this quarter’s survey had a specific focus on identifying key tasks that are important and difficult to the users. Before we jump into the results, here are some other interesting findings. + +## About Flutter users + +First, we learned that users are more satisfied with Flutter’s core framework (+2%) and ecosystem (+4%) than last quarter. But user satisfaction with the animation framework dropped slightly (-3%). To learn more about why this happened, we’ll ask a few more questions about your experience with the animation framework in the next survey. In the meantime, check out the “[Making Animations in Flutter](https://www.youtube.com/watch?v=GXIJJkq_H8g&list=PLjxrf2q8roU2v6UqYlt_KPaXlnjbYySua)” playlist from our YouTube channel. All of these videos were recently released. + +Next, about half of the respondents said that they’ve published apps with Flutter (51%). This number has been continuously growing — about a year ago, it was 34%. Those who published an app had a higher satisfaction rate with Flutter than those who haven’t. + +<DashImage figure src="images/0cqbZUVzw4XSlZyJR.webp" alt="*Satisfaction with Flutter by the stage of app development. Developers who have published a Flutter app were more satisfied with Flutter than those who haven’t.*" caption="*Satisfaction with Flutter by the stage of app development. Developers who have published a Flutter app were more satisfied with Flutter than those who haven’t.*" /> + + +Similarly, when we asked about their primary purpose for using Flutter, 48% of the respondents said that they are using Flutter for their main job. This number is also growing. A year ago, it was 30%. The number of users evaluating Flutter for potential production use has been decreasing, from 36% in 2019 to 25% in 2020. This implies that many of the users have completed evaluation and adopted Flutter for more serious purposes. + +Finally, we found that 69% of the developers who have published a complete production app are building both iOS and Android apps. 16% said that they are trying Web, in addition to the two mobile platforms. Currently, there are approximately 50,000 Flutter apps published in the Play Store. This number has been growing exponentially, and we hope to see this trend continue in the coming months. + +For more stats, insights, and future plans, see the “[Flutter Spring 2020 Update](https://medium.com/flutter/flutter-spring-2020-update-f723d898d7af)” article. + +## About tasks + +Some of the survey respondents were invited to take a longer survey. As a result, 43% of the respondents (N = 2,598) answered a set of questions where we asked for their thoughts on 29 Flutter-related tasks. The tasks covered UI development, framework and tooling, ecosystem, performance and troubleshooting, and testing and optimization. All 29 tasks in these areas were deemed to be critical to Flutter users. Yet, we hypothesized that some of the tasks are more important and difficult than others and thus require more attention from the team. + +We structured the survey so that a random half of the selected respondents were asked which of the tasks are important to them, and another half of the respondents were asked which of the tasks are difficult to accomplish. By dividing the respondents into two groups (aka “between-subject design”), we were able to reduce the total number of questions asked to each person. Also, by asking users to rate the importance and difficulty of the tasks, we are able to identify top priority tasks that are both important and difficult to Flutter developers. Below is the quadrant that the team used to decide what to do with each of the tasks. + +<DashImage figure src="images/0vABPU2mLHiRog8Vw.webp" alt="*Importance-difficulty quadrant supports decision making.*" caption="*Importance-difficulty quadrant supports decision making.*" /> + + +The following tasks were ranked to be the top 6 most important tasks. It is notable that the important tasks were widely spread across different stages of app development, from performance and testing to UI development. + +* Debug errors and crashes in my Flutter app + +* Test my app to ensure it works on the range of platforms + +* Choose a state management approach for my Flutter app + +* Understand and resolve layout issues (such as text overflow) + +* Create UI based on design spec provided by a UX UI designer + +* Troubleshoot platform-specific issues with my Flutter app + +<DashImage figure src="images/0OfKgvG4OS6Jt7Wum.webp" alt="*Relative importance of tasks. Blue dots are estimated importance scores for individuals, red dots are averages.*" caption="*Relative importance of tasks. Blue dots are estimated importance scores for individuals, red dots are averages.*" /> + + +The tasks below were ranked to be the top 6 most difficult tasks. We found several tasks related to troubleshooting and performance are difficult for Flutter professional developers. Most of the UI-related tasks were rated to be not very difficult. + +* Troubleshoot platform-specific issues with my Flutter app + +* Diagnose and fix memory issues in my Flutter app + +* Diagnose and fix CPU usage issues in my Flutter app + +* Add an existing platform-specific native API to my Flutter app + +* Diagnose and fix UI jank in my Flutter app + +* Develop a Flutter plugin (with a platform-specific implementation) + +<DashImage figure src="images/0nPtVnwI_F22HmC_F.webp" alt="*Relative difficulty of tasks. Blue dots are estimated importance scores for individuals, red dots are averages.*" caption="*Relative difficulty of tasks. Blue dots are estimated importance scores for individuals, red dots are averages.*" /> + + +As you can see, some tasks appeared in the list of both highly important and difficult tasks (such as “troubleshooting platform-specific issues with my Flutter app”). This knowledge is valuable to the Flutter team, because they can prioritize these areas based on user input, and not by guesswork. + +In addition, because Flutter usage is growing fast among enterprise customers (26% of the survey respondents work for an enterprise), we ran an additional analysis to find unique challenges that are specific to developers from larger companies. For this group of developers, we found that reducing app size, adding Flutter to an existing app (i.e. add-to-app), choosing state management techniques, understanding and resolving layout issues were more important and difficult than for other users. + +The Flutter team is now better aware of tasks that you consider both important *and* difficult and will use this input to guide development focus. In addition, the team is going to keep track of improvements over time, and will re-grade the tasks to ensure that each step of development is healthy and well-supported. + +## What’s next? + +One of the joys of working on an open source project like Flutter is having a community that is willing to offer insights and provide suggestions to improve the product, whether in surveys or by other forms of feedback such as GitHub issues. And so, we want to say a particular thank you to those of you who filled out the survey. Several of us read through every comment that was submitted. + +We hope that these reports demonstrate that we’re deeply interested in understanding your feedback. For the next quarterly survey, we plan to follow up on some of the insights from this survey, in particular we seek a better understanding of the tasks that were highlighted above as important yet difficult. Please look out for the next survey, which will be available soon. + +Flutter’s UX research team is also running a wide variety of user experience studies to make your experience with Flutter more pleasant. If you are interested in participating in future studies, please [sign up](https://docs.google.com/forms/d/e/1FAIpQLSe0i4De809KXVCdljGKrjMj3lxhuzbuFKCtY5PEQPCYtGxFMg/viewform). \ No newline at end of file diff --git a/sites/www/content/blog/what-can-we-do-better-to-improve-flutter-q2-2021-user-survey-results/images/0m2ihVMc99emY4hQY.webp b/sites/www/content/blog/what-can-we-do-better-to-improve-flutter-q2-2021-user-survey-results/images/0m2ihVMc99emY4hQY.webp new file mode 100644 index 0000000000..8a9c2f1560 Binary files /dev/null and b/sites/www/content/blog/what-can-we-do-better-to-improve-flutter-q2-2021-user-survey-results/images/0m2ihVMc99emY4hQY.webp differ diff --git a/sites/www/content/blog/what-can-we-do-better-to-improve-flutter-q2-2021-user-survey-results/index.md b/sites/www/content/blog/what-can-we-do-better-to-improve-flutter-q2-2021-user-survey-results/index.md new file mode 100644 index 0000000000..f9c8acf38d --- /dev/null +++ b/sites/www/content/blog/what-can-we-do-better-to-improve-flutter-q2-2021-user-survey-results/index.md @@ -0,0 +1,68 @@ +--- +title: "What can we do better to improve Flutter? — Q2 2021 user survey results" +description: "Written by JaYoung & Mariam" +publishDate: 2021-08-13 +author: jayoung-lee +image: images/0m2ihVMc99emY4hQY.webp +category: announcements +layout: blog +--- + +## What can we do better to improve Flutter? — Q2 2021 user survey results + +The Flutter team runs a user survey every quarter to hear from Flutter users and to make plans for the future. The most recent survey in May was our 13th user survey, which collected over 7,000 responses. We asked Flutter developers to rate their satisfaction with various parts of Flutter, and for those who were not fully satisfied, to comment as to why. The team not only summarized multiple choice survey responses, but also read open-ended comments that were left in the survey. There were 3,403 comments. And we’re back to share what we learned from you. + +## Satisfaction + +Overall, 92% of respondents said that they are positively satisfied with Flutter (39% somewhat satisfied and 53% very satisfied). Subsystems of Flutter scored different levels of satisfaction, ranging from 72% to 93%, as shown in the following graph. While we’re happy to share that we’re maintaining a high level of satisfaction, we’re also paying attention to specific areas where we can improve. + +<DashImage figure src="images/0m2ihVMc99emY4hQY.webp" /> + + +## Open-ended questions + +The survey had two open-ended questions: + +*“Why are you dissatisfied with Flutter? Please share your reasons:”* + +*“Is there anything else you’d like to tell the Flutter team?”* + +We collected 847 comments from the first question and 2,556 comments from the second question. The comments were first divided into various topics (such as documentation, ecosystem, web support, and so on) using a machine learning algorithm. Then, each topic was assigned to the teams working in each area for their analysis. + +Obviously, the teams cannot take on everything and solve all the issues at once. However, each team tried to extract insights from the comments and prioritize the ideas, carefully assessing the effect and effort needed. In the next sections, we’ll share some examples of our takeaways. + +### Example 1: About the general developer experience + +At a high level, reading through the written feedback was useful in three ways: + +1. Confirm existing issues + +1. Find new areas of concern and ideas + +1. Notice a need for additional information and evidence + +First, we confirmed that there are pain points frequently observed in many other sources, such as the GitHub Issues tab, Reddit, and Stack Overflow. The examples include, but aren’t limited to memory consumption and examples for skill-up. We’re well aware of these challenges and set priorities for them within each subteam. In the meantime, we’d like to re-promote existing resources like [Using the Memory View](https://flutter.dev/docs/development/tools/devtools/memory) or the [Learning Center](https://events.flutter.dev/engage/learn/beginner). + +Second, some topics were surprising for us to discover. For example, there were several comments that expressed concern about expanding to desktop and web. These comments asked us to concentrate on making iOS and Android better and to not stretch our focus. We understand the concern and will pay attention. We will also better communicate how we strive to be a strong technology for mobile as well as extend our support for additional platforms over time. + +Last, we wanted to have more information about some of the issues reported in the survey, especially those related to performance and documentation. If you want to have conversations with Flutter contributors and add more details to your issues, then consider posting them on the GitHub Issues tab. This tab is regularly triaged by the team. When you post, follow the rules in [File bugs for everything](https://github.com/flutter/flutter/wiki/Issue-hygiene#file-bugs-for-everything). (Of course, “how to” questions will be answered better in a support forum like Stack Overflow or Discord! See the [Community](https://flutter.dev/community) page for more info.) + +### Example 2: About Flutter’s web support + +The team found that the dissatisfaction feedback that was centered around lack of a stable version for web support was alleviated after the [stable release of web support ](https://medium.com/flutter/flutter-web-support-hits-the-stable-milestone-d6b84e83b425)with Flutter 2.0 in March. Many of the features that we added for the stable release of the web support for Flutter were based on last year’s [survey](https://medium.com/flutter/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020-f8034236b2a8), where we asked about the features that you might need for your web apps. + +The web-specific questions in the [Q3 2020 survey](https://medium.com/flutter/flutter-on-the-web-slivers-and-platform-specific-issues-user-survey-results-from-q3-2020-f8034236b2a8) were based on the open-ended feedback we received in our Q2 survey from last year. Your comments helped us determine which areas we should prioritize for the initial stable release for Flutter’s web support. + +In 2020, 15% of the comments were about performance and jank, so we prioritized improving our HTML renderer’s performance as well as stabilizing the CanvasKit renderer. Another 15% of the comments were about our plugin gaps and requests for GoogleMaps, Firestore, and so on, so we ensured that the majority of the Google-owned plugins, including GoogleMaps, Firebase_analytics, cloud_firestore and more, were supported in the web stable release. There were several comments regarding scrolling and text rendering. While there is still more work for us to do in these areas, we were able to build a solid foundation for supporting [scrolling on desktop form factors ](https://github.com/flutter/flutter/issues/70866)as well as supporting [rich text functionality](https://github.com/flutter/flutter/issues/33523), such as multiline text selection in a text field. + +This quarter, we hear that users believe Flutter’s web support still needs improvement. The “look and feel” of web apps is the most commonly mentioned reason for dissatisfaction with Flutter’s web support. We were also asked to provide more web-specific widgets, intuitive responsive widgets, and so on. Asks for search engine optimization (SEO) were also noted. Other reasons for dissatisfaction were right-to-left (RTL) text support, code size, routing, and debugging. + +We’re planning to ask about some of these areas in the next user survey, as we plan our next release. For others, we are already making progress, such as adding [RTL text support](https://github.com/flutter/flutter/projects/159), understanding issues with routing through our current [UXR study](https://github.com/flutter/uxr/tree/master/nav2-usability), and investigating ways to decrease our code size. + +## What’s next? + +The quarterly survey program is a structured way for us to better understand your needs, but it isn’t the only channel to use for us to hear from Flutter developers. If you have urgent issues that need follow-up, please file them on [GitHub](https://github.com/flutter/flutter/issues). + +The Flutter UXR team will continue to run surveys every quarter through an announcement on flutter.dev, in the Flutter IDE plugin, or on Twitter @FlutterDev. Please continue contributing your thoughts as the team seeks answers to important questions. You can also participate in other research by [signing up for upcoming UX studies](https://docs.google.com/forms/d/e/1FAIpQLSe0i4De809KXVCdljGKrjMj3lxhuzbuFKCtY5PEQPCYtGxFMg/viewform). + +Again, thank you to everyone who participated in this survey and provided valuable feedback. Our goal is to build a product that you love, and we appreciate your time and effort helping us. \ No newline at end of file diff --git a/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0DSPe0z8OcY1Dzlet.webp b/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0DSPe0z8OcY1Dzlet.webp new file mode 100644 index 0000000000..7854713f0c Binary files /dev/null and b/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0DSPe0z8OcY1Dzlet.webp differ diff --git a/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0JGPtcSX7QYbN8Dvn.webp b/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0JGPtcSX7QYbN8Dvn.webp new file mode 100644 index 0000000000..b1b4dd7a12 Binary files /dev/null and b/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0JGPtcSX7QYbN8Dvn.webp differ diff --git a/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0Kxtw9kjb1h_6DTAK.webp b/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0Kxtw9kjb1h_6DTAK.webp new file mode 100644 index 0000000000..26fadba162 Binary files /dev/null and b/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0Kxtw9kjb1h_6DTAK.webp differ diff --git a/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0LDgXRVH9t_ZteWDV.webp b/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0LDgXRVH9t_ZteWDV.webp new file mode 100644 index 0000000000..8973e513e9 Binary files /dev/null and b/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0LDgXRVH9t_ZteWDV.webp differ diff --git a/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0MjrAD-ZGebXA-xaX.webp b/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0MjrAD-ZGebXA-xaX.webp new file mode 100644 index 0000000000..1f88c391d4 Binary files /dev/null and b/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0MjrAD-ZGebXA-xaX.webp differ diff --git a/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0PceEjhOlGlSQw1oK.webp b/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0PceEjhOlGlSQw1oK.webp new file mode 100644 index 0000000000..4ed25af35e Binary files /dev/null and b/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0PceEjhOlGlSQw1oK.webp differ diff --git a/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0UdtJOiVqBwXOmDl_.webp b/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0UdtJOiVqBwXOmDl_.webp new file mode 100644 index 0000000000..7f6cd4d3f0 Binary files /dev/null and b/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/images/0UdtJOiVqBwXOmDl_.webp differ diff --git a/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/index.md b/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/index.md new file mode 100644 index 0000000000..9ed0092ee7 --- /dev/null +++ b/sites/www/content/blog/what-do-flutter-package-users-need-findings-from-q2-user-survey/index.md @@ -0,0 +1,91 @@ +--- +title: "What do Flutter package users need? Findings from Q2 user survey" +description: "We recently ran our sixth quarterly user survey and collected responses from over 7,000 Flutter users." +publishDate: 2019-07-25 +author: jayoung-lee +image: images/0JGPtcSX7QYbN8Dvn.webp +category: announcements +layout: blog +--- + +<DashImage figure src="images/0JGPtcSX7QYbN8Dvn.webp" alt="*A word cloud made with the Q2 survey comments* ☁️ *(link to the original [image](https://raw.githubusercontent.com/timsneath/wordcloud_flutter/master/flutter_wordcloud.png), [code](https://github.com/timsneath/wordcloud_flutter))*" caption="*A word cloud made with the Q2 survey comments* ☁️ *(link to the original [image](https://raw.githubusercontent.com/timsneath/wordcloud_flutter/master/flutter_wordcloud.png), [code](https://github.com/timsneath/wordcloud_flutter))*" /> + + +We recently ran our sixth quarterly user survey and collected responses from over 7,000 Flutter users. We found that 92.5% of the respondents are satisfied or very satisfied, which is slightly higher than the [last quarter](https://medium.com/flutter/insights-from-flutters-first-user-survey-of-2019-3659b02303a5)! We are thrilled to see a consistent level of satisfaction with Flutter. In this article, we cover some deep-dive questions around Flutter’s ecosystem, because we recognize that helping the Flutter community grow the ecosystem is important. + +As of July, 2019, you can find over 2,800 Flutter-dependent packages published on [pub.dev](https://pub.dev). At the same time last year, there were about 350 Flutter-dependent packages available, showing tremendous growth! And this does not include the thousands of additional Dart packages that are compatible with Flutter apps. + +Even though the ecosystem has been exploding, we recognize that there is still plenty of work remaining to build an excellent ecosystem around the Flutter project. To better understand the needs and struggles of our users, we asked a number of questions related to Flutter’s ecosystem in this quarter’s survey. We’re sharing the results in this article to help package authors build more useful packages that serve the needs of more users. + +Overall, 80.6% of 5,250 respondents were either *very satisfied* or *somewhat satisfied* with the Flutter ecosystem. This is not bad, but at the same time, it is one of the lower-scoring parts of the survey. + +<DashImage figure src="images/0MjrAD-ZGebXA-xaX.webp" alt="*Satisfaction with ecosystem*" caption="*Satisfaction with ecosystem*" /> + + +<DashImage figure src="images/0LDgXRVH9t_ZteWDV.webp" alt="*Overall satisfaction with Flutter*" caption="*Overall satisfaction with Flutter*" /> + + +When asked about the dissatisfaction with Flutter’s ecosystem, the reason selected by the most respondents was that “critical packages I need **do not exist** yet” (18%), which is perhaps to be expected for a relatively new technology. + +However, we are happy to find that our community is actively adding to the Flutter package ecosystem. 15% of respondents had experience developing packages for Flutter, and 59% had published their packages to pub.dev, the site for sharing packages written for Flutter and Dart apps. If you’ve written a package but have not published yet, you can read [Developing packages & plugins](https://flutter.dev/docs/development/packages-and-plugins/developing-packages) on [flutter.dev](http://flutter.dev), and contribute back to the Flutter community by publishing your package. It is not difficult — of those who had published to [pub.dev](http://pub.dev), 81% thought that it was *very easy* or *somewhat easy*. + +If you can’t decide which package to share with the Flutter community, visit the Flutter repository on GitHub and search for [issues labeled with “would be a good package”](https://github.com/flutter/flutter/issues?q=is%3Aopen+is%3Aissue+label%3A%22would+be+a+good+package%22+sort%3Areactions-%2B1-desc) to see what has been requested. You can upvote your favorite requests to increase their visibility. + +<DashImage figure src="images/0UdtJOiVqBwXOmDl_.webp" alt="*Reasons for dissatisfaction with Flutter’s ecosystem ( a multiple choice question)*" caption="*Reasons for dissatisfaction with Flutter’s ecosystem ( a multiple choice question)*" /> + + +However, there is an even better way to contribute to the ecosystem, if you’re interested in helping out. Note that all other reasons start with “critical packages I need do exist…”, meaning that package users were facing challenges even when packages exist. This tells us that we can improve the ecosystem by improving what is already there — by filing bugs, improving documentation, adding missing features, implementing support for the ‘other’ platform, adding tests, and so on. Consider finding a package that has potential but has not been loved enough and contribute towards it — with tests, bug reports, feature contributions, or examples! + +The most common reason for dissatisfaction with existing packages is that “they are not well **documented**” (17%). This is another area where the community can help. The survey question “What would you like done to improve your overall experience with the package ecosystem?” resulted in the following suggestions: + +* Include more diverse code usage examples + +* Include screenshots, animated gifs, or videos + +* Include a link to the corresponding code repository + +Here are some relevant quotes from the comment section: +> # *“There are still some packages that do not have code samples on the very first page. It should be mandatory to have at least a single simple example.”* +> # *“Emphasize to package developers to give more thorough examples of how to use their package.”* +> # *“Force all packages to have an animated gif or video demoing it (preferred) or a screenshot, and have an example Dart file.”* +> # *“A graphic display of an example package would be helpful. Many times it’s easier to see what a package is referring to than to run the example.“* +> # *“Would like to see the Example section filled out more often. Some packages don’t have any examples. Maybe have a clearer link on this page to the corresponding GitHub repo?”* + +Also, as shown in the graph above, difficulties associated with the actual use of packages (such as dependency issues, bugginess of packages, setup of packages) are relatively less concerning to users as compared to activities associated with selecting suitable packages (such as missing features, trustworthiness of publishers, guidance for choice, adequate platform support). + +The Flutter/Dart team at Google is also investigating ways to improve your experience with using, and contributing to, the ecosystem. Some of the options being considered include, but are not limited to: + +* Provide a better pub.dev search experience + +* Make it easy to tell which platform(s) a package supports + +* Offer more reliable quality metrics + +* Improve testability + +In the meantime, it might be worth pointing out that each package on pub.dev already receives scores for popularity, health, and maintenance; these scores help users gauge the quality of a package. You can find details of the scoring system on [pub.dev/help#scoring](https://pub.dev/help#scoring). + +<DashImage figure src="images/0DSPe0z8OcY1Dzlet.webp" alt="*Scoring example*" caption="*Scoring example*" /> + + +<DashImage figure src="images/0Kxtw9kjb1h_6DTAK.webp" alt="*Maintenance suggestions*" caption="*Maintenance suggestions*" /> + + +With the scoring system, package authors can understand what they can do to improve the quality of the package, and package users can estimate the quality (for example, the outdatedness) of a package. + +We expect the scoring system to expand over time to help users make more informed decisions. More specifically, we’d like to see test coverage added, and we’d like to expose better information about platform coverage, especially as the list of platforms that Flutter supports expands. We’d also like to provide a mark of whether a particular package is “recommended” so that users has a clear idea of what the Flutter community thinks is worth considering. As these scoring changes come about, we’ll communicate with our package authors to make sure that they have all of the information they need to meet the rising quality bar. + +We want to convey a huge thank you to the more than 7,000 Flutter users who filled out the long survey. We learned a lot — some other highlights are listed below. + +* Some Flutter users are not completely satisfied with the animation framework, not because it’s hard to achieve intended effects, but because it’s hard to get started. Respondents, especially new users, did not know where to begin, and it’s hard for them to understand how various concepts link together. Therefore, we are investing more into the learning materials for the animation framework. + +* For the API documentation on [api.flutter.dev](http://api.flutter.dev), sample code in the class doc was rated as the most useful resource. We have added full code samples to some of the classes in the API docs with the 1.7 release, but will continue expanding this feature to more classes. (We also accept PRs against the API docs on the [flutter/flutter repo](https://github.com/flutter/flutter/labels/d%3A%20api%20docs)!) + +<DashImage figure src="images/0PceEjhOlGlSQw1oK.webp" /> + + +* Lastly, many of you noticed that the number of unresolved issues in the GitHub repo is growing, which is an unfortunate side-effect of the exploding popularity of Flutter. While we closed over 1,250 issues in the last release, we have more work to do here. As mentioned in the Flutter 1.7 blog post, we’re working to increase staffing in this area, which will help with faster triaging of new bugs, faster attention to critical/crashing issues, closing and merging duplicate issues, and redirecting support requests to [StackOverflow](https://stackoverflow.com/questions/tagged/flutter). + +We value your responses to the survey and will use this information when determining work priorities. Please participate in our Q3 survey, which will be launched in August, and will explore new topic areas. + +Flutter’s UX research team performs a variety of user experience studies so that we can learn how to make your experience with Flutter more pleasant. If you are interested in participating, please [sign up](http://flutter.dev/research-signup) for future studies. \ No newline at end of file diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/03P8YpKBYQ5APySDe.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/03P8YpKBYQ5APySDe.webp new file mode 100644 index 0000000000..5418ba390f Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/03P8YpKBYQ5APySDe.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/05dBLaUz--b_5W2P3.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/05dBLaUz--b_5W2P3.webp new file mode 100644 index 0000000000..55b0f4fc78 Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/05dBLaUz--b_5W2P3.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0FjDr-VCUHT1A_0E3.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0FjDr-VCUHT1A_0E3.webp new file mode 100644 index 0000000000..0fdc3e5f3c Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0FjDr-VCUHT1A_0E3.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0P1coe65N82z6o0Ys.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0P1coe65N82z6o0Ys.webp new file mode 100644 index 0000000000..bc57396873 Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0P1coe65N82z6o0Ys.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0RVxVaxzNCEY3XbrU.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0RVxVaxzNCEY3XbrU.webp new file mode 100644 index 0000000000..c9e182b943 Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0RVxVaxzNCEY3XbrU.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0VdwCqbeqU3wlTMl-.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0VdwCqbeqU3wlTMl-.webp new file mode 100644 index 0000000000..5bb4259165 Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0VdwCqbeqU3wlTMl-.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0Wqiz9yZUhX2wXPl_.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0Wqiz9yZUhX2wXPl_.webp new file mode 100644 index 0000000000..585520ced2 Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0Wqiz9yZUhX2wXPl_.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0hllcpu-J2qiBOC-D.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0hllcpu-J2qiBOC-D.webp new file mode 100644 index 0000000000..eeb150f76f Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0hllcpu-J2qiBOC-D.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0uWwV77z9EcebWuzx.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0uWwV77z9EcebWuzx.webp new file mode 100644 index 0000000000..ce9ed5d32a Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0uWwV77z9EcebWuzx.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0zDQJ8f84eOugD5zY.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0zDQJ8f84eOugD5zY.webp new file mode 100644 index 0000000000..63421d3113 Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/0zDQJ8f84eOugD5zY.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/1BJJb_evaKM8qgJV5VCn95Q.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/1BJJb_evaKM8qgJV5VCn95Q.webp new file mode 100644 index 0000000000..9bb035db85 Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/images/1BJJb_evaKM8qgJV5VCn95Q.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/index.md b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/index.md new file mode 100644 index 0000000000..c5ace4ce34 --- /dev/null +++ b/sites/www/content/blog/what-we-learned-from-the-flutter-q2-2020-survey/index.md @@ -0,0 +1,104 @@ +--- +title: "What we learned from the Flutter Q2 2020 survey" +description: "With a focus on code samples, app performance, and writing packages" +publishDate: 2020-07-22 +author: jayoung-lee +image: images/1BJJb_evaKM8qgJV5VCn95Q.webp +category: announcements +layout: blog +--- + +For about ten days every quarter, the Flutter team hosts a survey to hear about your experience with Flutter. Each survey combines recurring questions that help our team monitor longitudinal trends of various measures, with new questions that collect timely feedback. It helps us stay focused on important areas. + +In this same tradition, we announced our ninth quarterly survey in May 2020 and were delighted that we received almost 8,000 responses over ten days, despite all the challenges of the current world situation. We are grateful for your participation. First, we are happy to share that 94% of you (that responded) are positively satisfied with Flutter; and 57% are *very* satisfied with Flutter, which is an all-time high! It’s humbling to see the love for the framework that we are building together. + +We learned about many topics in this quarter’s survey. Read on for some highlights! + +<DashImage figure src="images/1BJJb_evaKM8qgJV5VCn95Q.webp" alt="*57% of respondents are very satisfied with Flutter. In total, 94% are positively satisfied.*" caption="*57% of respondents are very satisfied with Flutter. In total, 94% are positively satisfied.*" /> + + +## Flutter adoption + +This quarter we were particularly interested in better understanding how professional developers choose Flutter, and so we asked some questions to find out more. Our hypothesis was that there’s an overlap between personal and professional use, and we wanted to test this and understand it better. + +<DashImage figure src="images/0hllcpu-J2qiBOC-D.webp" alt="*Overall, 46% of the respondents decided or convinced management to adopt Flutter after using Flutter for personal projects*" caption="*Overall, 46% of the respondents decided or convinced management to adopt Flutter after using Flutter for personal projects*" /> + + +We learned that 46% of respondents decided (or convinced management) to adopt Flutter after using Flutter for personal projects. It means that Flutter’s adoption is mostly driven by positive experiences at home that translated into work explorations. This is fairly consistent across different environments, as the chart below demonstrates. Even for those working for an agency or an enterprise, about a half the respondents said that they either decided or convinced management to adopt Flutter. + +We believe that this reflects the appeal of Flutter among actual users of the technology. In other words, you! + +<DashImage figure src="images/0FjDr-VCUHT1A_0E3.webp" alt="*The proportion of respondents who decided or convinced management remained high in all categories of company/organization*" caption="*The proportion of respondents who decided or convinced management remained high in all categories of company/organization*" /> + + +If you are in a similar situation where you want to convince others to adopt Flutter, you might consider referring to [Nubank’s tech blog](https://medium.com/building-nubank/https-medium-com-freire-why-nubank-chose-flutter-61b80b568772) and [video](https://www.youtube.com/watch?v=vnj2i6RNo3g&vl=en) about how they assessed Flutter and other technologies and decided to adopt Flutter. Nubank is the largest digital bank outside Asia with over 20 million customers, and their independent technical evaluation of Flutter makes interesting reading. + +## Code samples + +In the previous survey, users told us that sample code is the most useful learning resource, even more than videos, articles, or demos. In this quarter’s survey, we wanted to better understand what types of sample code we should invest in. + +In the survey, users were asked to recall their most recent experience of searching for sample code. Then they were asked about the details about the experience, such as where they found the most useful sample code. Stack Overflow was the most common place where people found useful sample code (26%), followed by GitHub (17%). + +<DashImage figure src="images/0zDQJ8f84eOugD5zY.webp" alt="*Stack Overflow was the common place where people found useful sample code from (26%), followed by GitHub (17%).*" caption="*Stack Overflow was the common place where people found useful sample code from (26%), followed by GitHub (17%).*" /> + + +At the end of this section of the survey, we asked which type of code would have been more useful, if only one type of code sample is available: more but smaller samples or fewer but larger samples. + +The overall results showed that the majority of the users (68%) want more samples, even if they are small. This result directly guides us to add multiple short examples first to the documentation when there are no code samples available in our documentation, such as API docs, cookbooks, tutorials, etc. + +<DashImage figure src="images/0VdwCqbeqU3wlTMl-.webp" alt="*Respondents said that more but smaller samples would be more useful. In the actual survey question, the phrase “{resource that participant selected}” was filled with suggested resources (such as Stack Overflow) where the respondent recently found sample code.*" caption="*Respondents said that more but smaller samples would be more useful. In the actual survey question, the phrase “{resource that participant selected}” was filled with suggested resources (such as Stack Overflow) where the respondent recently found sample code.*" /> + + +Is there any difference in the preferences towards the location of code samples, such as Stack Overflow or API docs? When we did a breakdown of the responses by the resource, the preference was still towards “more, but smaller code samples” in all the categories. + +<DashImage figure src="images/0Wqiz9yZUhX2wXPl_.webp" alt="*The proportion of the respondents who selected “more, but smaller code samples” remained high in all types of resources.*" caption="*The proportion of the respondents who selected “more, but smaller code samples” remained high in all types of resources.*" /> + + +Interestingly, as seen in the graph above, for the resources that users found useful in getting some inspiration or seeing an app known to be a good quality, such as GitHub or YouTube, the proportion of users preferring “fewer, but larger app samples” increased. This result may be helpful for the Flutter community instructors who are planning learning materials. + +## Flutter app performance + +Flutter compiles to machine code on mobile and desktop devices, allowing for performance comparable with system frameworks. Still, the Flutter team is invested in improving various performance metrics, including rendering speed, power usage, memory usage, and application size. As a result, the proportion of users positively satisfied with Flutter’s mobile performance increased from 85% in Q3 2019 to 92% this quarter. + +<DashImage figure src="images/0RVxVaxzNCEY3XbrU.webp" alt="*92% of the users are positively satisfied with Flutter’s mobile performance. This does not include 9.2% who selected “I don’t have enough experience with it to answer this question” or 2.3% who selected “What is release mode or profile mode?”*" caption="*92% of the users are positively satisfied with Flutter’s mobile performance. This does not include 9.2% who selected “I don’t have enough experience with it to answer this question” or 2.3% who selected “What is release mode or profile mode?”*" /> + + +In this quarter, we wanted to better understand how Flutter’s app performance is perceived by users, compared to those building Android and iOS apps with native platform frameworks. The questions were asked to those who said they have hands-on experience with Android and iOS development. + +While Flutter developers are generally happy with our performance and resource usage, app size is the area of greatest opportunity for improvement, with 11–13% of developers with prior Android or iOS experience rating it “unacceptable”. We have made improvements here, but this tells us we’re not yet done. We hope we can further optimize app size by tweaking our compilers and splitting AOT artifacts. + +<DashImage figure src="images/0uWwV77z9EcebWuzx.webp" alt="*Relative acceptability of Flutter apps performance, compared to Android native.*" caption="*Relative acceptability of Flutter apps performance, compared to Android native.*" /> + + +<DashImage figure src="images/03P8YpKBYQ5APySDe.webp" alt="*Relative acceptability of Flutter apps performance, compared to iOS native.*" caption="*Relative acceptability of Flutter apps performance, compared to iOS native.*" /> + + +We also learned that satisfaction with Flutter’s app memory usage is slightly lower than that of speed and power usage. To respond to this, we are improving memory performance (specifically image memory use), and at the same time, we are heavily investing into better tools, tests, and docs for all areas. The team is improving the DevTools’ UI as well as the documentation about the debugging process for testing memory, app size, and energy issues. + +## Writing packages + +Flutter developers can use packages on pub.dev to quickly build their apps, without having to write everything from scratch. While there are [over 10,000 Flutter-compatible packages](https://pub.dev/flutter/packages) available on pub.dev, only a small number of them are directly maintained by the Google Flutter team. Thus, Flutter’s ecosystem is, by design, heavily dependent on community contributions. + +We are dedicated to building a great development environment that supports package authors’ creation and curation of high-quality packages. Just this week, we launched a complete [redesign of the pub.dev site](https://medium.com/dartlang/pub-dev-redesign-747406dcb486) based on previous feedback. + +Now our user-experience researchers want to understand what motivates package authors to publish and maintain packages. We talked to numerous package authors earlier this year, and based on those findings, we added quantitative questions to this survey to explore the area further. + +The main finding from the survey was that most package authors publish packages with altruistic motives, but maintain the packages because of their own needs. For instance, 82% of authors published their packages because they wanted other users to benefit, but the same reason motivated only 58% of the authors to maintain them. On the other hand, 66% of authors published packages for their own use, and the same reason made 70% of the authors maintain them. + +<DashImage figure src="images/05dBLaUz--b_5W2P3.webp" alt="*82% of the authors published their packages because they wanted other users to benefit*" caption="*82% of the authors published their packages because they wanted other users to benefit*" /> + + +<DashImage figure src="images/0P1coe65N82z6o0Ys.webp" alt="*“Benefiting others” motivated only 58% of the authors to maintain their packages*" caption="*“Benefiting others” motivated only 58% of the authors to maintain their packages*" /> + + +It made us realize that there are additional ways we could support this. Some package authors might benefit from knowing how much impact you could have on the community by publishing certain packages. For instance, you might want to know how many Flutter users would benefit from a notification plugin. (Currently, you can see what potential packages are waiting for authors from the [flutter/flutter](https://github.com/flutter/flutter) repository, with the “[would be a good package](https://github.com/flutter/flutter/issues?q=is%3Aopen+label%3A%22would+be+a+good+package%22+sort%3Areactions-%2B1-desc)” label.) Also, you might want to know which packages are explicitly looking for contributions, so that you can easily find the packages in need. Thanks to your input, we will consider work in this area for future pub.dev releases. + +We also discovered that we could support the learning aspect better, because many users pointed out that learning opportunities motivate them to maintain packages. Learning how to write documentation, learning what to improve from peer developers, or learning how to manage an open source project may all fall under this category. With these findings in mind, we will continue to create an environment where you can improve yourself as you contribute to the community. + +## Wrapping up + +We hope that this series of posts continues to demonstrate how much we value your input. We spend hours analyzing and digesting your responses every quarter. Several of us from across user research, engineering, and product management read through every free-form comment, and while we can’t answer every one directly, your needs and comments influence our decision-making every day. + +We are going to continue this tradition of basing our decisions on what you tell us in these surveys. So please continue to participate and share your thoughts. Special thanks go to the 7,883 users who participated in the Q2 survey and helped us create the insights shared here. + +Flutter’s UX research team is also running a wide variety of user experience studies to make your experience with Flutter more pleasant. For instance, last quarter, we contacted and interviewed package authors to hear about their experience of publishing and maintaining packages. If you are interested in participating in such studies, please [sign up](https://docs.google.com/forms/d/e/1FAIpQLSe0i4De809KXVCdljGKrjMj3lxhuzbuFKCtY5PEQPCYtGxFMg/viewform) to be considered for a future study. \ No newline at end of file diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/08Ewc1lTs1hnn1Y0w.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/08Ewc1lTs1hnn1Y0w.webp new file mode 100644 index 0000000000..38d6fb5cd3 Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/08Ewc1lTs1hnn1Y0w.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0CfG7t7ePTA-LlwyZ.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0CfG7t7ePTA-LlwyZ.webp new file mode 100644 index 0000000000..b595a1811e Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0CfG7t7ePTA-LlwyZ.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0DWsq2C8LftK67nYq.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0DWsq2C8LftK67nYq.webp new file mode 100644 index 0000000000..dc38b9237d Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0DWsq2C8LftK67nYq.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0WHlC27Td3vNoiDFc.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0WHlC27Td3vNoiDFc.webp new file mode 100644 index 0000000000..cc1170f8ef Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0WHlC27Td3vNoiDFc.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0XaZXV1SPtqbuZhFc.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0XaZXV1SPtqbuZhFc.webp new file mode 100644 index 0000000000..fe180a2a16 Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0XaZXV1SPtqbuZhFc.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0hdD0EJyfncwlgAtW.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0hdD0EJyfncwlgAtW.webp new file mode 100644 index 0000000000..4e853245b8 Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0hdD0EJyfncwlgAtW.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0jYeqpOHh1OgqKmqW.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0jYeqpOHh1OgqKmqW.webp new file mode 100644 index 0000000000..7ec1118879 Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0jYeqpOHh1OgqKmqW.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0rbSxslPyUG71IB-Y.webp b/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0rbSxslPyUG71IB-Y.webp new file mode 100644 index 0000000000..64591949bf Binary files /dev/null and b/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/images/0rbSxslPyUG71IB-Y.webp differ diff --git a/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/index.md b/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/index.md new file mode 100644 index 0000000000..e0700a2efb --- /dev/null +++ b/sites/www/content/blog/what-we-learned-from-the-flutter-q3-2022-survey/index.md @@ -0,0 +1,79 @@ +--- +title: "What we learned from the Flutter Q3 2022 survey" +description: "About Firebase SDKs, quick fixes and refactorings, and target platforms" +publishDate: 2022-12-08 +author: jayoung-lee +image: images/0XaZXV1SPtqbuZhFc.webp +category: announcements +layout: blog +--- + +More than 10,000 Flutter developers have been participating in the quarterly user survey in the past couple years to give feedback to the Flutter team. The most recent quarter was no exception — the Flutter team wanted to collect developer feedback about four topics: 1) Firebase SDKs, 2) quick fixes and refactorings, 3) target platforms, and 4) desktop support. Of these four topics, the survey presented each respondent with two random topics. Thanks to those who responded! + +This article covers the results about the first three topics. We’ll cover the last topic (desktop development) in another article. + +Before jumping into the results of the first three special topics, we’d like to report that Flutter developers continue to love the framework. Of the survey respondents, 55% are *very satisfied* with Flutter, and an additional 38% are *somewhat satisfied* with Flutter. This brings the total of satisfied developers to 93%. This has been consistent over the last three quarters. The percentage of *very satisfied* developers had a slight drop to 55%. We plan to track this metric and study what might reduce developer satisfaction from *very satisfied* to *somewhat satisfied*. + +<DashImage figure src="images/0jYeqpOHh1OgqKmqW.webp" alt="Figure 1. Satisfaction with Flutter over time" caption="Figure 1. Satisfaction with Flutter over time" /> + + +As shown in **Figure 2**, our respondents love the core framework, Android support, and Material widgets. Our biggest opportunity lies with Cupertino widgets and web support. We continue to study what’s needed to improve developer experience in these areas and further invest to advance it. If you want to participate in future user studies, you can sign up at [flutter.dev/research-signup](https://flutter.dev/research-signup). + +<DashImage figure src="images/0XaZXV1SPtqbuZhFc.webp" alt="Figure 2. Satisfaction with subsystems" caption="Figure 2. Satisfaction with subsystems" /> + + +## Firebase SDKs for Flutter + +Flutter developers use [Firebase SDKs for Flutter](https://pub.dev/publishers/firebase.google.com/packages) (aka FlutterFire) for tools and services that Firebase provides. These include authentication, databases, analytics, and messaging. Currently, fifteen stable [Firebase plugin packages](https://firebase.google.com/docs/flutter/setup?platform=ios#available-plugins) are available on pub.dev. Because of the great adoption of these packages, we wanted to explore what kind of learning materials would increase their usability. + +The most useful information we learned was the problems developers had when trying to use various Firebase packages. As shown in **Figure 3**, Developers cited “not enough real-world examples” as the most common issue that they faced when trying a new Firebase package (14.2%). This was particularly problematic for complex packages with many moving parts, like `firebase_messaging` or `firebase_dynamic_links`. To address this, we’ll be adding more [sample apps to the Firebase docs](https://firebase.google.com/docs/samples). We’re adding two types of sample apps. The first, “quickstart samples”, will cover the use of a specific Firebase package. The second will provide more complex, real-world example apps that use multiple Firebase packages. + +<DashImage figure src="images/0hdD0EJyfncwlgAtW.webp" alt="Figure 3. Problems with adopting a new Firebase SDK for Flutter" caption="Figure 3. Problems with adopting a new Firebase SDK for Flutter" /> + + +In the survey, a number of developers also selected “API design was too complicated or inconvenient”. This finding was consistent across many packages, like `firebase_database`, `firebase_in_app_messaging`, including the packages mentioned in the previous section. We found this result notable. Though some APIs are simple and small, the concepts they cover, like asynchronous data, aren’t. With this in mind, we will strive to simplify learning materials. Furthermore, we will aim to make new and existing APIs for tools and services as easy to understand as possible. + +“Overall quality of documentation was low” was the last of the top three issues. To respond to this need, we plan to improve the Firebase documentation for Flutter developers. We will add new sections in the Firebase documentation for the Firebase UI packages, such as Firebase UI for Auth, and more code samples throughout. The changes will roll out throughout the coming quarters. + +## Quick fixes and refactorings + +In the [first survey of 2022](https://medium.com/flutter/does-flutter-boost-developer-productivity-475f713724b3), developers indicated that quick fixes and refactorings are the top missing or insufficiently-supported features in our IDEs. As you can see in **Figure 4**, we support quick fixes and refactorings in the IDE, but we wanted to identify *why* developers think that the IDEs are missing or not supporting these features. + +<DashImage figure src="images/08Ewc1lTs1hnn1Y0w.webp" alt="Figure 4. Quick fixes and refactorings in VS Code" caption="Figure 4. Quick fixes and refactorings in VS Code" /> + + +First, we learned that more than half of the respondents think that quick fixes and refactorings work fine (53.1%), as shown in **Figure 5**. Yet, 34.6% said more features need to be added. This was almost three times more than the number of developers who asked us to fix bugs (12.4%). + +<DashImage figure src="images/0CfG7t7ePTA-LlwyZ.webp" alt="Figure 5. The current status of quick fixes and refactorings" caption="Figure 5. The current status of quick fixes and refactorings" /> + + +We then asked those who want new features what general category of quick fixes or refactoring features they needed. Before designing the question, we reviewed all the relevant GitHub issues and did a quick [Twitter survey](https://twitter.com/craig_labenz/status/1547650452023435%20267?s=20&t=hOjRR_RImDoyIP38CqsjLA) to organize various feature requests into groups. You can see the list of groups in **Figure 6.** + +<DashImage figure src="images/0rbSxslPyUG71IB-Y.webp" alt="Figure 6. Importance of quick fixes and various refactorings features" caption="Figure 6. Importance of quick fixes and various refactorings features" /> + + +From this question, we learned that respondents found adding more quick fixes for “fixing compilation errors, warnings, or lints” to be more important than others (76.4% “very important”), though it doesn’t reduce the importance of other features. We also learned that respondents found “extracting or moving widgets” to be more important than other refactorings (69.2% “very important”). We now know that, although we support extracting and moving widgets, developers want more options, such as extracting a widget to a new file. This matches various GitHub issues filed: [flutter/flutter-intellij/issues/5591](https://github.com/flutter/flutter-intellij/issues/5591), [Dart-Code/Dart-Code/issues/1831](https://github.com/Dart-Code/Dart-Code/issues/1831), [flutter-intellij/issues/4540](https://github.com/flutter/flutter-intellij/issues/4540), [dart-lang/sdk/issues/35767](https://github.com/dart-lang/sdk/issues/35767), and others. In addition, developers also want more features for restructuring libraries, classes, or methods (61.9% “very important”), as well as managing imports (59.7% “very important”). + +All of this information was greatly valued by the Dart Analyzer team, who will be reviewing the diagnostics to identify opportunities for additional fixes. The team has already started adding some new fixes for diagnostics (to be released with Dart 2.19), with more planned for future releases. + +## Target platforms + +Flutter developers can build production quality apps for six platforms: Android, iOS, web, Windows, macOS, and Linux. In this section of the survey, we wanted to know which of the six platforms developers target, and whether they use Flutter for multiplatform development at a team-level. Our questions asked first about the platforms on which developers write code and then asked about additional platforms their team targets. + +As shown in **Figure 7**, most developers target Android (91.7%), iOS (61.3%), and web (35.5%). Some overlap occurred, as this question allowed for multiple answers. Counting the number of platforms selected, only 24% of the developers targeted only one platform in the last month. Of the respondents, 76% developed for two or more platforms, with(32% developing for three or more). + +<DashImage figure src="images/0WHlC27Td3vNoiDFc.webp" alt="Figure 7. Target platforms developers are actively building and app for" caption="Figure 7. Target platforms developers are actively building and app for" /> + + +In addition, we wanted to know how many of the developers were on a development team, where teammates developed for a platform that’s different from their own. For example, developer A targets Android and developer B targets iOS. As shown in **Figure 8**, 40% of the developers said they had a coworker who’s working on a different platform than theirs. This increased to 61%, when we filtered for the developers who said they themselves were developing for only one platform. + +<DashImage figure src="images/0DWsq2C8LftK67nYq.webp" alt="Figure 8. Teammates developing for an additional platform" caption="Figure 8. Teammates developing for an additional platform" /> + + +When combined, we could calculate that 91% of the Flutter teams are active multiplatform development teams (24%*0.61 + 76%). At least 72% of the respondents developed apps for both Android and iOS. Of the total respondents, 34% targeted iOS, Android, and the web and 12% targeted iOS, Android, the web, and Windows. + +## What’s next? + +We want to deliver a great development experience that maximizes productivity. The insights we gain from this and other surveys are invaluable. We’ll use these results to prioritize engineering tasks and education content that’s useful to the Flutter developers. + +We’ll be back in 2023 with more survey results. Before then, we will publish a separate article about what we learned from the research about desktop support. See you in the next article! \ No newline at end of file diff --git a/sites/www/content/blog/what-weve-learned-from-the-july-2018-flutter-user-survey/images/11iG_hmhGXRwV4ZakI_cVjg.webp b/sites/www/content/blog/what-weve-learned-from-the-july-2018-flutter-user-survey/images/11iG_hmhGXRwV4ZakI_cVjg.webp new file mode 100644 index 0000000000..2d10989572 Binary files /dev/null and b/sites/www/content/blog/what-weve-learned-from-the-july-2018-flutter-user-survey/images/11iG_hmhGXRwV4ZakI_cVjg.webp differ diff --git a/sites/www/content/blog/what-weve-learned-from-the-july-2018-flutter-user-survey/images/1dYcTfPO6MS8RWpr9KB1cgg.webp b/sites/www/content/blog/what-weve-learned-from-the-july-2018-flutter-user-survey/images/1dYcTfPO6MS8RWpr9KB1cgg.webp new file mode 100644 index 0000000000..02e520e2ca Binary files /dev/null and b/sites/www/content/blog/what-weve-learned-from-the-july-2018-flutter-user-survey/images/1dYcTfPO6MS8RWpr9KB1cgg.webp differ diff --git a/sites/www/content/blog/what-weve-learned-from-the-july-2018-flutter-user-survey/images/1g2MLIDuEz3HdUy1_JkjbVA.jpeg b/sites/www/content/blog/what-weve-learned-from-the-july-2018-flutter-user-survey/images/1g2MLIDuEz3HdUy1_JkjbVA.jpeg new file mode 100644 index 0000000000..d0cda6b695 Binary files /dev/null and b/sites/www/content/blog/what-weve-learned-from-the-july-2018-flutter-user-survey/images/1g2MLIDuEz3HdUy1_JkjbVA.jpeg differ diff --git a/sites/www/content/blog/what-weve-learned-from-the-july-2018-flutter-user-survey/images/1gM8BhAAqil2B79RCarJ5-A.jpeg b/sites/www/content/blog/what-weve-learned-from-the-july-2018-flutter-user-survey/images/1gM8BhAAqil2B79RCarJ5-A.jpeg new file mode 100644 index 0000000000..6d00d9be94 Binary files /dev/null and b/sites/www/content/blog/what-weve-learned-from-the-july-2018-flutter-user-survey/images/1gM8BhAAqil2B79RCarJ5-A.jpeg differ diff --git a/sites/www/content/blog/what-weve-learned-from-the-july-2018-flutter-user-survey/index.md b/sites/www/content/blog/what-weve-learned-from-the-july-2018-flutter-user-survey/index.md new file mode 100644 index 0000000000..9f40284e01 --- /dev/null +++ b/sites/www/content/blog/what-weve-learned-from-the-july-2018-flutter-user-survey/index.md @@ -0,0 +1,64 @@ +--- +title: "What We’ve Learned from the July 2018 Flutter User Survey" +description: "Flutter’s User Experience Research team recently conducted an online survey to measure user satisfaction and understand user needs." +publishDate: 2018-08-30 +author: jayoung-lee +image: images/1gM8BhAAqil2B79RCarJ5-A.jpeg +category: announcements +layout: blog +--- + +*Written by [Ja Young Lee](https://medium.com/@jayoung.lee) & [Tao Dong](https://twitter.com/taodong)* + +<DashImage figure src="images/1gM8BhAAqil2B79RCarJ5-A.jpeg" alt="Photo by [Jess Watters](https://unsplash.com/@designedbyjess) on [Unsplash](https://unsplash.com/)" caption="Photo by [Jess Watters](https://unsplash.com/@designedbyjess) on [Unsplash](https://unsplash.com/)" /> + + +Flutter’s User Experience Research team recently conducted an online survey to measure user satisfaction and understand user needs. The survey was open to the public for two weeks in July of 2018 and received responses from 1,016 users. Thank you so much for giving us your valuable feedback! In this post, we would like to share what we learned from the survey and what we plan to do to address your feedback. + +## **Who’s using Flutter?** + +As you may expect, Flutter users come from all kinds of backgrounds. When we asked about their experience in different mobile platforms (a multiple choice question), Android was the most popular (67%), followed by Web (45%) and iOS (30%). We saw a sizable growth in the proportion of Flutter users coming from a Web background, given that it was 33% in the survey in April. While the ratio of Android developers and iOS developers largely reflects the [global user base imbalance of these two technologies](https://insights.stackoverflow.com/survey/2018/#technology-platforms), we are working on ways to attract more iOS developers to the Flutter community. + +<DashImage figure src="images/1dYcTfPO6MS8RWpr9KB1cgg.webp" /> + + +## **More Flutter apps coming in the wild?** + +We also found that 15% of the respondents have already published a Flutter app, and more excitingly, 52% are working on an app to be released within the next 6 months! We are looking forward to seeing these beautiful apps roll out to millions of mobile users. + +<DashImage figure src="images/11iG_hmhGXRwV4ZakI_cVjg.webp" /> + + +## **Are users satisfied with Flutter?** + +Our users were generally satisfied with Flutter. 92% of the respondents said that they are very satisfied or somewhat satisfied with Flutter. They were also willing to recommend Flutter to others; more than half (56%) of the respondents gave 9 or 10 to the likeliness of recommending Flutter to others, on a scale of 0 to 10. + +When it comes to fast development and beautiful UIs that Flutter strives to support, 79% found Flutter extremely helpful or very helpful in both reaching their maximum engineering velocity and implementing an ideal UI. This is the first time we asked this question in our user survey. We will continue to monitor and strengthen our core value proposition — “build beautiful native apps in record time” — in future surveys and other types of research. + +## How about Flutter’s documentation? + +Documentation is a key aspect of great developer experience. We’ve seen users praise how well Flutter is documented, yet from a previous survey documentation is also the area that users wanted us to do more. So in this survey, we had a section of questions devoted to better understanding documentation needs and pain points from our users. + +The results show that the quality of Flutter’s existing documentation is highly regarded by users (93% of respondents consider the quality good, very good, or excellent). However, there is clearly room for adding more sample code, making it easier to find samples, and improving the ability to learn how to use widgets from API references. + +In addition, hundreds of users took the time to leave comments in this section of the survey. Thank you so much! We held a workshop to read through these comments and discuss what we can do to make Flutter better. In this workshop, we organized comments into high-level topics and sub-topics. From this exercise, it’s clear that having example snippets in each widget’s API doc is important. We’re making progress, and we welcome community contributions to our docs. Adding a missing snippet to a widget’s API doc is one of the simplest yet impactful contributions you can make to the Flutter ecosystem. If you’re interested in contributing, refer to the [Flutter repo style guide](https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#provide-sample-code) and get started with pages listed in [this bug](https://github.com/flutter/flutter/issues/21136). + +The results also reinforced what we found earlier in our UX studies — having visual illustrations in documentation helps developers learn Flutter more effectively. Finally, we’ve received many comments requesting better guidance on state management. We’re actively working on more samples and articles on this very topic. + +We took all this feedback to heart. We were also heartened by some compliments users wrote in the survey. For example: +> *“Flutter is amazing, it’s made my job as a programmer infinitely more enjoyable. Please keep up the great work!”* +> *“This framework is absolutely awesome!!! I have built very complicated UI in no time, the load time is fantastic! especially in comparison to nativescript, also the layout is extremely flexible and easy to use. I am so happy!!!”* +> *“I’m incredibly satisfied with flutter and the community. I’m looking forward to a desktop solution as well.”* + +<DashImage figure src="images/1g2MLIDuEz3HdUy1_JkjbVA.jpeg" /> + + +## And are there more Cupertino widgets? + +This short answer is Yes! In this survey, we asked users who had experience using Cupertino widgets to tell us what we can do to improve the ability to build UIs in the iOS style. The top request was for more Cupertino widgets. We’re happy to report that we have made significant improvements over the past few months. For example, we recently added [CupertinoApp](https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/cupertino/app.dart), a convenience widget that wraps a number of widgets that are commonly required for an iOS-themed application. We’ll continue improving the Cupertino library based on community feedback. + +## What’s up next? + +We thank everyone who filled out the survey. The Flutter team has learned a lot, both quantitatively and qualitatively. We plan to run a user survey regularly to learn from you as we improve and grow. + +The announcement of the survey was made on [Twitter](https://twitter.com/flutterio), [Reddit](https://www.reddit.com/r/FlutterDev/), [Google Groups](https://groups.google.com/d/forum/flutter-dev), [Flutter documentation website](https://flutter.io/docs/), and the [Gitter chat room](https://gitter.im/flutter/flutter). Join our community, and keep your eye out for the next one! You can also [sign up](https://flutter.io/research-signup) for future user experience studies. \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0-Ichbk_jGmhmdj3B.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0-Ichbk_jGmhmdj3B.webp new file mode 100644 index 0000000000..8379fefdb4 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0-Ichbk_jGmhmdj3B.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/02bw4yhTolgWmIv1V.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/02bw4yhTolgWmIv1V.webp new file mode 100644 index 0000000000..c97a9bee94 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/02bw4yhTolgWmIv1V.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/03GGvtXibNB3-X8jA.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/03GGvtXibNB3-X8jA.webp new file mode 100644 index 0000000000..77e80f5ec0 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/03GGvtXibNB3-X8jA.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0HwoWeVZall4b0VB5.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0HwoWeVZall4b0VB5.webp new file mode 100644 index 0000000000..f2fd84f33a Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0HwoWeVZall4b0VB5.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0I9GWJb-XsUhJUKrH.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0I9GWJb-XsUhJUKrH.webp new file mode 100644 index 0000000000..ee0e4aaef2 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0I9GWJb-XsUhJUKrH.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0IOR7QJ3FOqaGWIn9.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0IOR7QJ3FOqaGWIn9.webp new file mode 100644 index 0000000000..b5b9f0e24c Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0IOR7QJ3FOqaGWIn9.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0KQZef4s2-koToHMA.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0KQZef4s2-koToHMA.webp new file mode 100644 index 0000000000..6be840a50f Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0KQZef4s2-koToHMA.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0N86bmi_l8Zx-UOnE.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0N86bmi_l8Zx-UOnE.webp new file mode 100644 index 0000000000..52fe957e5a Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0N86bmi_l8Zx-UOnE.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0O7zZDDpTc9cK-2X0.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0O7zZDDpTc9cK-2X0.webp new file mode 100644 index 0000000000..33cabcd4b1 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0O7zZDDpTc9cK-2X0.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0RPj9uHm2n8Zx1w1X.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0RPj9uHm2n8Zx1w1X.webp new file mode 100644 index 0000000000..86698a336c Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0RPj9uHm2n8Zx1w1X.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0W1aGfw-fJXfFiqWR.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0W1aGfw-fJXfFiqWR.webp new file mode 100644 index 0000000000..6a085f83fa Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0W1aGfw-fJXfFiqWR.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0YRyYVK1ObQ252jSL.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0YRyYVK1ObQ252jSL.webp new file mode 100644 index 0000000000..04a8c97920 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0YRyYVK1ObQ252jSL.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0eQD12Fr7liueGZXF.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0eQD12Fr7liueGZXF.webp new file mode 100644 index 0000000000..9eab751a81 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0eQD12Fr7liueGZXF.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0gqIT_20hrbHlG8d_.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0gqIT_20hrbHlG8d_.webp new file mode 100644 index 0000000000..06e48346dd Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0gqIT_20hrbHlG8d_.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0h4J8qtxzJXTg769G.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0h4J8qtxzJXTg769G.webp new file mode 100644 index 0000000000..5162ded8e5 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0h4J8qtxzJXTg769G.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0htJEMRQ54MN7hfcl.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0htJEMRQ54MN7hfcl.webp new file mode 100644 index 0000000000..57d91e0961 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0htJEMRQ54MN7hfcl.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0huNKqtnnHVtbYW3_.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0huNKqtnnHVtbYW3_.webp new file mode 100644 index 0000000000..dfc6914142 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0huNKqtnnHVtbYW3_.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0n6Lap1ZNfF-RL-Dd.gif b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0n6Lap1ZNfF-RL-Dd.gif new file mode 100644 index 0000000000..34b4b22b9b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0n6Lap1ZNfF-RL-Dd.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0oW2lFF9KTVUTaXeo.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0oW2lFF9KTVUTaXeo.webp new file mode 100644 index 0000000000..7cc1b85efc Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0oW2lFF9KTVUTaXeo.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0q1ug8VRx6ewqU_Ks.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0q1ug8VRx6ewqU_Ks.webp new file mode 100644 index 0000000000..6eb79d7787 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0q1ug8VRx6ewqU_Ks.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0rGuUHqseWvUJ2FPl.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0rGuUHqseWvUJ2FPl.webp new file mode 100644 index 0000000000..ad88a710b4 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0rGuUHqseWvUJ2FPl.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0sm8nQOLfY63_D6p8.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0sm8nQOLfY63_D6p8.webp new file mode 100644 index 0000000000..5723e00423 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0sm8nQOLfY63_D6p8.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0uxIQpd8XEMIsyePK.gif b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0uxIQpd8XEMIsyePK.gif new file mode 100644 index 0000000000..ee97ae3ae2 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0uxIQpd8XEMIsyePK.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0vGJ5uq6V58RaGO5o.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0vGJ5uq6V58RaGO5o.webp new file mode 100644 index 0000000000..1f2d7df79a Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0vGJ5uq6V58RaGO5o.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0vu31pLbeCjWZvvJI.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0vu31pLbeCjWZvvJI.webp new file mode 100644 index 0000000000..93590b59ae Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0vu31pLbeCjWZvvJI.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/0xoGJox6uQxSbN3G3.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0xoGJox6uQxSbN3G3.webp new file mode 100644 index 0000000000..11407da8b8 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/0xoGJox6uQxSbN3G3.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/1CsdmVrVcS7jYSBzldzIzzg.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/1CsdmVrVcS7jYSBzldzIzzg.webp new file mode 100644 index 0000000000..d541adaaa6 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/1CsdmVrVcS7jYSBzldzIzzg.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/images/1Ky80dLtF0OQ81U3ohMc5jg.webp b/sites/www/content/blog/whats-new-in-flutter-2-0/images/1Ky80dLtF0OQ81U3ohMc5jg.webp new file mode 100644 index 0000000000..f84a64432e Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-0/images/1Ky80dLtF0OQ81U3ohMc5jg.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-0/index.md b/sites/www/content/blog/whats-new-in-flutter-2-0/index.md new file mode 100644 index 0000000000..c8ea8d840f --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-2-0/index.md @@ -0,0 +1,439 @@ +--- +title: "What’s New in Flutter 2.0" +description: "Flutter web and Null Safety move to stable, Flutter desktop moves to beta and so much more!" +publishDate: 2021-03-03 +author: csells +image: images/0KQZef4s2-koToHMA.webp +category: releases +layout: blog +--- + +## What’s New in Flutter 2 + +Today, we’re pleased to announce the release of Flutter 2. It’s been a little more than two years since the Flutter 1.0 release, but in that short time, we’ve closed 24,541 issues and merged 17,039 PRs from 765 contributors. Just since the Flutter 1.22 release in September, we’ve closed 5807 issues and merged 4091 PRs from 298 contributors. Special thanks go out to our volunteer contributors who generously give their spare time to improve the Flutter project. The top volunteer contributors for the Flutter 2 release were [xu-baolin](https://github.com/xu-baolin) with 46 PRs, [a14n](https://github.com/a14n) with 32 PRs that focused on bringing Flutter to null safety, and [hamdikahloun](https://github.com/hamdikahloun) with 20 PRs that improved a number of the Flutter plugins. But it’s not just coders that contribute to the Flutter project; a great set of volunteer PR reviewers were also responsible for reviewing 1525 PRs, including [hamdikahloun](https://github.com/hamdikahloun) (again!), [CareF](https://github.com/CareF) and [YazeedAlKhalaf](https://github.com/YazeedAlKhalaf) (who’s only 16!). Flutter is truly a community effort and we couldn’t have gotten to version 2 without the issue raisers, PR contributors, and code reviewers. This release is for all of you. + +There are a lot of exciting things happening as part of the Flutter 2 release. For an overview of what’s new across Flutter 2 and Dart 2.12, as well as how our customers and partners are using Flutter 2, see [Announcing Flutter 2](https://developers.googleblog.com/2021/03/announcing-flutter-2.html). For a detailed look at Dart 2.12, see [Announcing Dart 2.12](https://medium.com/dartlang/announcing-dart-2-12-499a6e689c87). For how best to take advantage of Flutter web now that it’s recommended for production use, see [Flutter web support hits the stable milestone](https://medium.com/flutter/web-post-d6b84e83b425). + +And, to see what’s new in Flutter 2 itself, keep reading! + +## Web + +As of today, Flutter’s web support has transitioned from beta to the stable channel. With this initial stable release, Flutter pushes reusability of code to another level with the support of the web platform. So now when you create a Flutter app in stable, web is just another device target for your app. + +<DashImage figure src="images/0KQZef4s2-koToHMA.webp" alt="[Moi Mobiili](https://www.moi.fi/), a modern Mobile Virtual Network Operator striving for operational excellence through digitalization, chose Flutter to build their Mun Moi Account manager app and recently launched their web app." caption="[Moi Mobiili](https://www.moi.fi/), a modern Mobile Virtual Network Operator striving for operational excellence through digitalization, chose Flutter to build their Mun Moi Account manager app and recently launched their web app." /> + + +By leveraging the web platform’s many strengths, Flutter built a foundation for building rich interactive web applications. We primarily focused on performance and improvements to our rendering fidelity. In addition to our HTML renderer, we added a new CanvasKit-based renderer. We’ve also added web-specific features, such as a [Link widget](https://pub.dev/documentation/url_launcher/latest/link/Link-class.html), to make sure your app running in the browser feels like a web app. + +Find more details about this stable release in [Flutter’s web support blog post](https://medium.com/flutter/web-post-d6b84e83b425). + +## Sound Null Safety + +Sound null safety is a significant addition to the Dart language, which further strengthens the type system by distinguishing nullable types from non-nullable types. This enables developers to prevent null error crashes, a common reason for apps to crash. By incorporating null checks into the type system, these errors can be caught during development, thus preventing crashes in production. Sound null safety is fully supported in stable as of Flutter 2, which contains Dart 2.12. For more details, see the [Dart 2.12 blog post](https://medium.com/dartlang/announcing-dart-2-12-499a6e689c87). + +The pub.dev package repository already has [more than 1,000 null safe](https://pub.dev/packages?q=&null-safe=1) packages published, including hundreds of packages by the [Dart](https://pub.dev/packages?q=publisher%3Adart.dev&sort=popularity&null-safe=1), [Flutter](https://pub.dev/packages?q=publisher%3Aflutter.dev&sort=popularity&null-safe=1), [Firebase](https://pub.dev/packages?q=publisher%3Afirebase.google.com&sort=popularity&null-safe=1), and [Material](https://pub.dev/packages?q=publisher%3Amaterial.io&sort=popularity&null-safe=1) teams. If you are a package author, review the [migration guide](https://dart.dev/null-safety/migration-guide) and consider migrating today. + +## Desktop + +In this release, we’re pleased to announce that Flutter’s desktop support is available in the stable channel under an early release flag. What this means is that we’re ready for you to give it a try as a deployment target for your Flutter apps: you can think of it as a “beta snapshot” that previews the final stable release coming later this year. + +To bring Flutter desktop to this degree of quality, there have been improvements both big and small, starting with working to ensure that text editing operates like the native experience on each of the supported platforms, including foundational features like [text selection pivot points](https://github.com/flutter/flutter/pull/71756) and being able to stop propagation of[ a keyboard event once it’s been handled](https://github.com/flutter/flutter/issues/33521). On the mouse input side, dragging with a high precision pointing device now starts immediately instead of waiting for the lag needed when handling touch input. Also, a built-in context menu has been added to the TextField and TextFormField widgets for [Material](https://github.com/flutter/flutter/pull/74286) and [Cupertino](https://github.com/flutter/flutter/pull/73882) design languages. Finally, [grab handles have been added](https://github.com/flutter/flutter/pull/74299) to the ReorderableListView widget. + +<DashImage figure src="images/0uxIQpd8XEMIsyePK.gif" alt="The ReorderableListView now has grab handles for easy drag ’n’ drop with a mouse" caption="The ReorderableListView now has grab handles for easy drag ’n’ drop with a mouse" /> + + +The ReorderableListView was always good at moving items around with very little effort on your part as a developer, but it required the user to initiate a drag using a long-press. That made sense on a mobile device, but few desktop users would think to long-press on an item with their mouse to move it around, so this release includes a grab handle suitable for mouse or touch input. Another improvement for platform-idiomatic functionality is [an updated scrollbar](https://github.com/flutter/flutter/pull/71664) that shows up correctly for the desktop form-factor. + +<DashImage figure src="images/0n6Lap1ZNfF-RL-Dd.gif" alt="This release includes an updated Scrollbar widget that works great in a desktop environment" caption="This release includes an updated Scrollbar widget that works great in a desktop environment" /> + + +The Scrollbar widget has been updated to provide the interactive features that are expected on the desktop, including the ability to drag the thumb, click on the track to page up and down, and to show a track when the mouse hovers over any part of the scrollbar. Furthermore, since the Scrollbar is themeable using [the new ScrollbarTheme class](https://api.flutter.dev/flutter/material/ScrollbarTheme-class.html), you can style it to match your app’s look and feel. + +For additional desktop-specific functionality, this release also enables command-line argument handling for Flutter apps so that simple things like a double-click on a data file in the Windows File Explorer can be used to open the file in your app. We’ve also worked hard to make resizing much smoother for both [Windows](https://github.com/flutter/engine/pull/23701) and [macOS](https://github.com/flutter/engine/pull/23924), and to enable IME (Input Method Editors) for international users. + +<DashImage figure src="images/0W1aGfw-fJXfFiqWR.webp" alt="Flutter desktop now supports intuitive IME input" caption="Flutter desktop now supports intuitive IME input" /> + + +In addition, we have provided [updated docs on what you need to do to begin preparing your desktop app for deployment to the appropriate OS-specific stores](https://flutter.dev/desktop#distribution). Give them a try and please provide feedback if we’ve missed anything. + +When it comes to trying the beta for Flutter desktop, you can access it by switching to the beta channel as expected as well as setting the config flags for the platforms you’re targeting according to [the directions on flutter.dev](https://flutter.dev/desktop#create-a-new-project). In addition, we’ve also made a snapshot of the beta bits available on the stable channel. If you use ‘flutter config’ to enable one of the desktop config settings (for example, `enable-macos-desktop`), then you can try out the beta functionality of the desktop support w/o having to go through the lengthy process of moving to the beta channel and pulling down all the latest beta of the Flutter SDK, building the tools, etc. This is great for giving it a try or using the desktop support as a simple “Flutter Emulator.” + +However, if you choose to stay on the stable channel to access the desktop beta, you won’t get new features or bug fixes as quickly as switching to the beta or dev channels. So, if you’re actively targeting Windows, macOS, or Linux, we recommend switching to a channel that provides updates more quickly. + +As we approach our first full production-quality release of Flutter desktop, we know we have more to do, including support for integration with native top-level menus, text editing that feels more like the experience of the individual platforms, and accessibility support, as well as general bug fixes and performance enhancements. If there are other things you think need doing before the desktop moves to production quality, [please be sure to provide your feedback](http://github.com/flutter/flutter/issues). + +## Platform Adaptive Apps: Flutter Folio Sample + +Now that Flutter supports three platforms for production apps (Android, iOS, and the web) and three more in beta (Windows, macOS, and Linux), a natural question arises: how do you write an app that adapts itself *well* to multiple different form factors (small, medium, and large screens), different input modes (touch, keyboard, and mouse) and different idioms (mobile, web, and desktop)? To answer this question for ourselves as well as for Flutter developers everywhere, we commissioned the Flutter Folio scrapbooking app. + +<YoutubeEmbed id="x4xZkdlADWo" title="Flutter Folio Trailer" fullwidth="true"/> + + +Folio is meant to be a simple example of an app that you’d want to run well on multiple platforms from a single code base. And by “well,” we mean that it looks good on small, medium, and large screens, that it takes advantage of touch, keyboard, and mouse input, and that it works well for the idioms of the platform, (for example, by using links on the web and menus on desktop). We call this kind of app “platform adaptive” because it adapts well to whatever platform it’s running on. + +If you’d like to see how you can make your own apps platform adaptive, you can check out [the source code for Folio](https://github.com/gskinnerTeam/flutter-folio). In the future, expect to find docs and codelabs that explore this topic more deeply. In the meantime, check out Aloïs Deniel’s [excellent blog post and video](https://aloisdeniel.com/#/posts/adaptative-ui) on this very topic. + +## Google Mobile Ads to Beta + +In addition to Flutter desktop moving to beta, today we’re excited to announce an open beta for[ Google Mobile Ads SDK for Flutter](https://pub.dev/packages/google_mobile_ads). This is a brand new plugin that provides inline banner and native ads, in addition to the existing overlay formats (overlay banner, interstitial, and rewarded video ads). This plugin unifies support for Ad Manager and Admob, so no matter what size publisher you are, this plugin can be tailored to your scenarios. + +<DashImage figure src="images/0h4J8qtxzJXTg769G.webp" /> + + +We’ve been piloting this plugin with some of our early customers in a private beta program, and many of them have successfully launched their apps with these new formats. For example, Sua Musica (largest Latin American music platform for independent artists with more than 15k verified artists and 10M MAU) launched their new Flutter app with the Google Mobile Ads SDK for Flutter plugin. They saw an 350% increase on Impressions with a 43% increase on CTR and 13% increase on eCPM. + +This [plugin](https://pub.dev/packages/google_mobile_ads) is available for you to use today. As part of Flutter Engage, Andrew Brogdon and Zoey Fan presented a session on “Monetizing apps with Flutter” ([available on the Flutter Engage](http://goo.gle/EngageOnDemand) site), where they talk about monetization strategies for apps built with Flutter, and how you can load ads in your Flutter app. Moreover, we created a new [Ads](http://flutter.dev/ads) page on flutter.dev where you can find all helpful resources such as the [plugin implementation guide](https://developers.google.com/admob/flutter), the [inline banner and native ads](https://codelabs.developers.google.com/codelabs/admob-inline-ads-in-flutter) codelab, and the [overlay banner, interstitial and rewarded video ads](https://codelabs.developers.google.com/codelabs/admob-ads-in-flutter#0) codelab. Please be sure to check them out! + +## New iOS features + +Just because we’re continuing to increase the quality of support for other platforms, don’t think we’re forgetting iOS. In fact, this release brings with it 178 PRs merged that are iOS-related, including [23495](https://github.com/flutter/engine/pull/23495), which brings State Restoration to iOS, [67781](https://github.com/flutter/flutter/pull/67781), which fulfills a long-standing request to build an IPA directly from the command line without opening Xcode, and [69809](https://github.com/flutter/flutter/pull/69809), which updates the CocoaPods version to match the latest tooling. In addition, a few iOS widgets have been added to the Cupertino design language implementation. + +A new [CupertinoSearchTextField](https://api.flutter.dev/flutter/cupertino/CupertinoSearchTextField-class.html) offers the iOS search bar UI. + +<DashImage figure src="images/0sm8nQOLfY63_D6p8.webp" /> + + +The [CupertinoFormSection](https://api.flutter.dev/flutter/cupertino/CupertinoFormSection-class.html), [CupertinoFormRow](https://api.flutter.dev/flutter/cupertino/CupertinoFormRow-class.html), and [CupertinoTextFormFieldRow](https://api.flutter.dev/flutter/cupertino/CupertinoTextFormFieldRow-class.html) widgets make it easier to produce validated form fields with iOS’s sectioned visual aesthetics. + +<DashImage figure src="images/0HwoWeVZall4b0VB5.webp" /> + + +In addition to the feature work for iOS, we’re continuing to [research performance improvements](https://github.com/flutter/flutter/issues/60267#issuecomment-762786388) for iOS and Flutter in general when it comes to shaders and animation. iOS continues to be a premier platform for Flutter and we’ll continue to work to bring important new features and performance improvements. + +## New widgets: Autocomplete and ScaffoldMessenger + +This release of Flutter comes with two additional new widgets, AutocompleteCore and ScaffoldMessenger. [AutocompleteCore](https://github.com/flutter/flutter/pull/62927) represents the minimal functionality required to get auto-complete functionality into your Flutter app. + +<DashImage figure src="images/03GGvtXibNB3-X8jA.webp" /> + + +Autocomplete is an often-requested feature for Flutter, so this release starts to provide this functionality. You can use it today, but if you’re curious about the design for the complete feature, check out [the autocomplete design document](https://docs.google.com/document/d/1fV4FDNdcza1ITU7hlgweCDUZdWyCqd-rjz_J7K2KkfY/). + +Likewise, [the ScaffoldMessenger](https://github.com/flutter/flutter/pull/64101) was created to deal with a number of SnackBar-related issues, including the ability to easily create a SnackBar in response to an AppBar action, creating SnackBars to persist between Scaffold transitions, and being able to show SnackBars at the completion of an asynchronous action, even if the user has navigated to a page with a different Scaffold. + +<DashImage figure src="images/0vGJ5uq6V58RaGO5o.webp" /> + + +All of this goodness can be yours with a couple of lines of code that you should be using from now on to display your SnackBars: + +``` +final messenger = ScaffoldMessenger.of(context); +messenger.showSnackBar(SnackBar(content: Text(‘I can fly.’))); +``` + + +As you might imagine, there’s more to it than that; for the details, check out [an excellent video from Kate Lovett on ScaffoldMessenger](https://www.youtube.com/watch?v=sYG7HAGu_Eg). + +## Multiple Flutter instances with Add-to-App + +We know from talking with many Flutter developers that a significant number of you don’t have the luxury of starting a brand new app but you *can* take advantage of Flutter by adding it to your existing iOS and Android apps. This feature, called [Add-to-App](https://flutter.dev/docs/development/add-to-app), is an excellent way to reuse your Flutter code across both mobile platforms while still preserving your existing native code base. However, for those of you doing that, we’ve sometimes heard that it’s unclear how to go beyond integrating the first screen into Flutter. Interweaving Flutter and native screens makes navigation states difficult to maintain, and integrating multiple Flutters at the view level uses a lot of memory. + +In the past, additional Flutter instances had the same memory cost as the first instance. In Flutter 2, we’ve reduced the static memory cost of creating additional Flutter engines by ~99% to ~180kB per instance. + +<DashImage figure src="images/0huNKqtnnHVtbYW3_.webp" /> + + +The new APIs to enable this are in preview on the beta channel and are [documented on flutter.dev](https://flutter.dev/docs/development/add-to-app/multiple-flutters) along with [a set of sample projects demonstrating this new pattern](https://github.com/flutter/samples/tree/master/add_to_app/multiple_flutters). With this change, we no longer hesitate to recommend creating multiple instances of the Flutter engine in your native apps. + +## Flutter Fix + +Whenever any framework matures and gathers users with larger and larger code bases, the tendency over time is to avoid making any changes to the framework API to avoid breaking an increasing number of lines of code. With over 500,000 Flutter developers across a growing number of platforms, Flutter 2 is quickly falling into this category. However, for us to continue to improve Flutter over time, we want to be able to make breaking changes to the API. The question becomes, how to continue to improve the Flutter API without breaking our developers? + +Our answer is [Flutter Fix](http://flutter.dev/docs/development/tools/flutter-fix). + +Flutter Fix is a combination of things. First, there’s a new command-line option to the `dart` CLI tool called `dart fix`that knows where to look for a list of deprecated APIs and how to update code using those APIs. Second, it’s the list of available fixes itself, which is bundled with the Flutter SDK as of version 2. And finally, it’s an updated set of Flutter extensions for the VS Code, IntelliJ,and Android Studio IDEs that know how to expose that same list of available fixes as quick fixes with little light bulbs that will help you to change the code with a click of your mouse. + +As an example, let’s say you had the following line of code in your app: + +<DashImage figure src="images/1Ky80dLtF0OQ81U3ohMc5jg.webp" alt="Creating a Flutter widget using a deprecated argument" caption="Creating a Flutter widget using a deprecated argument" /> + + +Because the argument to this constructor is deprecated, it should be replaced with the following: + +<DashImage figure src="images/1CsdmVrVcS7jYSBzldzIzzg.webp" alt="Creating a Flutter widget with the deprecated argument replaced" caption="Creating a Flutter widget with the deprecated argument replaced" /> + + +Even if you’re familiar with all of the many Flutter deprecations, the larger the number of changes you have to make in your code, the harder it is for you to apply all of the fixes and the easier it is to make mistakes; humans are not great at these kinds of repetitive tasks. But computers are; by executing the following command, you can see all of the fixes we know how to make across your entire project: + +``` +$ dart fix --dry-run +``` + + +If you’d like to apply them in bulk, you can easily do so: + +``` +$ dart fix --apply +``` + + +Or, if you’d like to apply these fixes interactively in your favorite IDE, you can do that, too. + +<DashImage figure src="images/0I9GWJb-XsUhJUKrH.webp" /> + + +While we’ve been marking old APIs as deprecated for a number of years now, now that we have [a policy about when we will actually remove deprecated APIs](https://medium.com/flutter/deprecation-lifetime-in-flutter-e4d76ee738ad), Flutter 2 is the first time that we’ve done so. Even though we haven’t yet captured all of the deprecated APIs as data to feed Flutter Fix, we continue to add more from previously deprecated APIs and will continue to do so with future breaking changes. Our goal is to do our best to make Flutter’s API the best it can be while also keeping your code up to date as we do it. + +## Flutter DevTools + +To make it clear that DevTools is a tool that should be used for debugging your Flutter apps, we’ve renamed it to be Flutter DevTools when it’s debugging a Flutter app. In addition, we’ve done a lot of work to bring it to production quality worthy of Flutter 2. + +One new feature that helps you zero in on your issues even before you’ve launched DevTools is the ability for Android Studio, IntelliJ, or Visual Studio Code to notice when there’s a common exception and offer to bring it up in DevTools to help you debug it. For example, the following shows an overflow exception has been thrown in your app, which brings up an option in Visual Studio Code to debug the issue in DevTools. + +<DashImage figure src="images/0O7zZDDpTc9cK-2X0.webp" alt="The Flutter IDE extensions notice when your app throws a layout overflow exception" caption="The Flutter IDE extensions notice when your app throws a layout overflow exception" /> + + +Pressing that button brings you right to the Flutter Inspector in DevTools on the widget that’s causing the trouble so you can fix it. We’re only doing this for layout overflow exceptions today but our plan is to include this kind of handling for all kinds of common exceptions for which DevTools can be the solution. + +Once you’ve got DevTools running, new error badges on the tabs help you track down specific issues in your app. + +<DashImage figure src="images/0q1ug8VRx6ewqU_Ks.webp" alt="The red dots in DevTools helps you focus on the parts of your app with errors" caption="The red dots in DevTools helps you focus on the parts of your app with errors" /> + + +Another new feature in DevTools is the ability to easily see an image that’s at a higher resolution than it’s displayed, which helps track down excessive app size and memory usage. To enable this feature, enable the Invert Oversized Images in the Flutter Inspector. + +<DashImage figure src="images/0xoGJox6uQxSbN3G3.webp" alt="Enable the “Invert Oversized Images” option to highlight images that are bigger than they need to be" caption="Enable the “Invert Oversized Images” option to highlight images that are bigger than they need to be" /> + + +Now, when you display an image that’s significantly larger in resolution than its display size, it will appear upside-down to make it easy to find in your app. + +<DashImage figure src="images/0gqIT_20hrbHlG8d_.webp" alt="The “Invert Oversized Images” option in action" caption="The “Invert Oversized Images” option in action" /> + + +Also, by popular demand, in addition to showing details about flexible layouts in the Flutter Inspector’s Layout Explorer, we’ve added the capability to show fixed layouts as well, enabling you to debug layouts of all kinds. + +<DashImage figure src="images/0oW2lFF9KTVUTaXeo.webp" alt="The new Layout Explorer shows layout details for fixed as well as flex layouts" caption="The new Layout Explorer shows layout details for fixed as well as flex layouts" /> + + +And that’s not all. Here’s just a summary of a few more of the new features in Flutter DevTools 2: + +* Added average FPS information and usability improvements to the Flutter frames chart + +* Calling out failed network requests in the network profiler with red error labels + +* New memory view charts are faster, smaller and easier to use, including a new hovercard to describe activity at a specific time + +* Added search and filtering to the Logging tab + +* Track logs from before DevTools is started so you can see the complete logging history when you do start it up + +* Renamed the “Performance” view to “CPU Profiler” to make it more clear what functionality it provides + +* Added timing grid to CPU Profiler flame charts + +* Renamed the “Timeline” view to “Performance” to make it more clear what functionality it provides + +And that’s *still* not all. For the full set of changes, I recommend the following announcements: + +* DevTools [0.9.4](https://groups.google.com/g/flutter-announce/c/mx_hBxuXM9Q/m/Kjy9dqS3AAAJ) + +* DevTools [0.9.5](https://groups.google.com/g/flutter-announce/c/mNqTNPUwBKw/m/_1qyXwTBAQAJ) + +* DevTools [0.9.6](https://groups.google.com/g/flutter-announce/c/Ta5HR39P3go/m/2a43w7iSCwAJ) + +* DevTools [0.9.7](https://groups.google.com/g/flutter-announce/c/IJ97oJ2HpxM/m/909J9Kl8AQAJ) + +* DevTools [2.0](https://groups.google.com/g/flutter-announce/c/0xQhJR4nQbI) + +## Android Studio/IntelliJ Extension + +The Flutter plugin for the IntelliJ family of IDEs has gained a number of new features for Flutter 2 as well. To start, there’s a new project wizard, which matches the new wizard style in IntelliJ. + +<DashImage figure src="images/0RPj9uHm2n8Zx1w1X.webp" /> + + +<DashImage figure src="images/0vu31pLbeCjWZvvJI.webp" /> + + +Also, if you’re using IntelliJ or Android Studio on Linux to program against the [Flutter SDK installed from the Snap Store](https://snapcraft.io/flutter), the Flutter snap path has been added to the list of known SDK paths. This makes it easier for users of the Flutter snap to configure the Flutter SDK in Settings. Thanks to MarcusTomlinson@ for this contribution! + +<DashImage figure src="images/0N86bmi_l8Zx-UOnE.webp" alt="Android Studio on Linux is easier to use with the Flutter SDK installed via Snap" caption="Android Studio on Linux is easier to use with the Flutter SDK installed via Snap" /> + + +There’s a lot more good stuff you can read about in the announcements for the recent updates: + +* IntelliJ Plugin [M51](https://groups.google.com/g/flutter-announce/c/w65rD73R83Q/m/gV5p0qf2AAAJ) + +* IntelliJ Plugin [M52](https://groups.google.com/g/flutter-announce/c/tQqqMOIg6V0/m/wj7Kbv4-AgAJ) + +* IntelliJ Plugin [M53](https://groups.google.com/g/flutter-announce/c/V335xbsPWUs/m/14LSp05kAQAJ) + +* IntelliJ Plugin [M54](https://groups.google.com/g/flutter-announce/c/-jYDrwG7PmA) + +## Visual Studio Code Extension + +The Flutter extension for Visual Studio Code has improved for Flutter 2 as well, starting with a number of testing enhancements, including the ability to re-run just failed tests. + +<DashImage figure src="images/0htJEMRQ54MN7hfcl.webp" /> + + +After two years in development, the LSP (Language Server Protocol) support for Dart is rolling out now as the default way to get to the Dart analyzer for integration into Visual Studio Code for the Flutter extension. The LSP support enables a number of improvements for Flutter development, including the ability to apply all fixes of a certain kind in the current Dart file and to cause code completion to generate complete function calls, including parenthesis and required arguments. + +<DashImage figure src="images/0YRyYVK1ObQ252jSL.webp" /> + + +<DashImage figure src="images/0eQD12Fr7liueGZXF.webp" /> + + +And the LSP support isn’t just for Dart; it also supports code completion in the `pubspec.yaml` and `analysis_options.yaml` files. + +<DashImage figure src="images/0IOR7QJ3FOqaGWIn9.webp" /> + + +These are just a few of the updates that the Visual Studio Code extension for Flutter has enjoyed recently. You can read the full list in these announcements: + +* Visual Studio Code Plugin [v3.16](https://dartcode.org/releases/v3-16/) + +* Visual Studio Code Plugin [v3.17](https://dartcode.org/releases/v3-17/) + +* Visual Studio Code Plugin [v3.18](https://dartcode.org/releases/v3-18/) + +* Visual Studio Code Plugin [v3.19](https://dartcode.org/releases/v3-19/) + +* Visual Studio Code Plugin [v3.20](https://dartcode.org/releases/v3-20/) + +## DartPad updated to support Flutter 2 + +This list of tooling updates would not be complete without a mention of DartPad, which has been updated to support Flutter 2. + +<DashImage figure src="images/02bw4yhTolgWmIv1V.webp" alt="DartPad has been updated for Flutter 2" caption="DartPad has been updated for Flutter 2" /> + + +Now you can try the new null safe version of Flutter without leaving the comfort of your favorite browser. + +## Ecosystem updates + +The Flutter development experience includes more than the framework and the tools; it also includes the wide range of packages and plugins available for Flutter apps. In the time since the last Flutter stable release, much has happened in that space as well. For example, between the camera and the video_player plugins, nearly 30 PRs have been merged to greatly increase the quality of both. If you’ve had trouble using either of these in the past, you should have another look; we think you’ll find them much more robust. + +Also, if you’re a Firebase user, we are pleased to announce that the most popular plugins have been brought to production quality, including null safety support, and [a full set of reference documentation and common usage tutorials](http://firebase.flutter.dev) for Android, iOS, web, and macOS. These plugins include the following: + +* Core + +* Authentication + +* Cloud Firestore + +* Cloud Functions + +* Cloud Messaging + +* Cloud Storage + +* Crashlytics + +Also, if you’re looking for crash reporting for your app, you may want to consider Sentry, which has [announced a new SDK for Flutter apps](https://blog.sentry.io/2021/03/03/with-flutter-and-sentry-you-can-put-all-your-eggs-in-one-repo/). + +<DashImage figure src="images/0rGuUHqseWvUJ2FPl.webp" alt="The Sentry crash reporting tool now supports Flutter" caption="The Sentry crash reporting tool now supports Flutter" /> + + +With Sentry’s SDK for Flutter, you can be notified of errors that occur on Android, iOS, or native platforms in real-time. You can see the details in [the Sentry documentation](https://docs.sentry.io/platforms/flutter/). + +In addition, if you haven’t yet seen [the Flutter Community “plus” plugins](http://plus.fluttercommunity.dev/), you’ll want to check them out. They’ve forked a number of the popular plugins initially developed by the Flutter team and have added null safety support, support for additional platforms and a whole new set of [documentation](https://plus.fluttercommunity.dev/docs/overview/) as well as beginning to fix appropriate issues from the flutter/plugins repository. This plugins include the following: + +* Android Alarm+ + +* Android Intent+ + +* Battery+ + +* Connectivity+ + +* Device Info+ + +* Network Info+ + +* Package Info+ + +* Sensors+ + +* Share+ + +At this point, the set of Flutter-compatible packages and plugins numbers greater than 15,000, which makes it hard to find those you should consider first. For this reason, we publish the Pub Points (static analysis scoring), popularity rank, likes and, for exceptionally high quality, a special designation for those packages marked as a [Flutter Favorite](https://flutter.dev/docs/development/packages-and-plugins/favorites). In time for Flutter 2, we’ve added several new packages to the list of favorites: + +* animated_text_kit + +* bottom_navy_bar + +* chopper + +* font_awesome_flutter + +* flutter_local_notifications + +* just_audio + +Congratulations to the authors of these packages! If you haven’t checked them out yet, or [the rest of the packages on the list](https://pub.dev/flutter/favorites), you should. + +And last but certainly not least, for package authors or package users interested in whether the latest version of a package works on a recent version of Flutter, you’ll want to check out Codemagic’s new [pub.green](http://pub.green) site. + +<DashImage figure src="images/0-Ichbk_jGmhmdj3B.webp" alt="Codemagic’s new [pub.green](http://pub.green) site shows compatibility for the top packages with recent Flutter releases" caption="Codemagic’s new [pub.green](http://pub.green) site shows compatibility for the top packages with recent Flutter releases" /> + + +The pub.green site tests the compatibility of Flutter and Dart packages available on pub.dev with different Flutter versions. Think of it as the “can I use” for Flutter. For more details, I recommend [the announcement blog post from the CodeMagic team](https://blog.codemagic.io/pub-green/). + +## Breaking Changes + +We’ve made the following breaking changes for Flutter 2, many of which can be automatically mitigated using the `dart fix` command or the quick fixes in your IDE of choice: + +* [61366](https://github.com/flutter/flutter/pull/61366) Continue the clipBehavior breaking change + +* [66700](https://github.com/flutter/flutter/pull/66700) Default FittedBox’s clipBehavior to none + +* [68905](https://github.com/flutter/flutter/pull/68905) Remove nullOk parameter from Cupertino color resolution APIs + +* [69808](https://github.com/flutter/flutter/pull/68908) Remove nullOk from Scaffold.of and ScaffoldMessenger.of, create maybeOf for both + +* [68910](https://github.com/flutter/flutter/pull/68910) Remove nullOk parameter from Router.of and make it return a non-nullable value + +* [68911](https://github.com/flutter/flutter/pull/68911) Add maybeLocaleOf to Localizations + +* [68736](https://github.com/flutter/flutter/pull/68736) Remove nullOK in Media.queryOf + +* [68917](https://github.com/flutter/flutter/pull/68917) Remove nullOk parameter from Focus.of, FocusTraversalOrder.of, and FocusTraversalGroup.of + +* [68921 Remove nullOk parameter from Shortcuts.of, Actions.find, and Actions.handler](https://github.com/flutter/flutter/pull/68921) + +* [68925](https://github.com/flutter/flutter/pull/68925) Remove nullOk parameter from AnimatedList.of and SliverAnimatedList.of + +* [69620](https://github.com/flutter/flutter/pull/69620) Remove deprecated methods from BuildContex + +* [70726](https://github.com/flutter/flutter/pull/70726) Remove the nullOk parameter from Navigator.of and add Navigator.maybeOft + +* [72017](https://github.com/flutter/flutter/pull/72017) Remove deprecated CupertinoTextThemeData.brightness + +* [72395](https://github.com/flutter/flutter/pull/72395) Remove deprecated [PointerEnterEvent, PointerExitEvent].fromHoverEvent + +* [72532](https://github.com/flutter/flutter/pull/72532) Remove deprecated showDialog.child + +* [72890](https://github.com/flutter/flutter/pull/72890) Remove deprecated Scaffold.resizeToAvoidBottomPadding + +* [72893](https://github.com/flutter/flutter/pull/72893) Remove deprecated WidgetsBinding.[deferFirstFrameReport, allowFirstFrameReport] + +* [72901](https://github.com/flutter/flutter/pull/72901) Remove deprecated StatefulElement.inheritFromElement + +* [72903](https://github.com/flutter/flutter/pull/72903) Remove deprecated Element methods + +* [73604](https://github.com/flutter/flutter/pull/73604) Remove deprecated CupertinoDialog + +* [73745](https://github.com/flutter/flutter/pull/73745) Remove deprecated actionsForegroundColor from Cupertino[Sliver]NavigationBar + +* [73746](https://github.com/flutter/flutter/pull/73746) Remove deprecated ButtonTheme.bar + +* [73747](https://github.com/flutter/flutter/pull/73747) Remove span deprecations + +* [73748](https://github.com/flutter/flutter/pull/73748) Remove deprecated RenderView.scheduleInitialFrame + +* [73749](https://github.com/flutter/flutter/pull/73749) Remove deprecated Layer.findAll + +* [75657](https://github.com/flutter/flutter/pull/74657) Remove vestigial nullOk parameter from Localizations.localeOf + +* [74680](https://github.com/flutter/flutter/pull/74680) Remove nullOk from Actions.invoke, add Actions.maybeInvoke + +## Summary + +In closing, from all of us here on the Flutter Team at Google we want to say — thank you. Thank you ×150,000 for the more than 150,000 Flutter apps launched in the community in just the past two years. In case you missed it, we compiled a few of our favorite Flutter apps in this Mashup Video dedicated to you, the community, played at the opening of the Flutter Engage keynote. + +<YoutubeEmbed id="A3ltMaM6noM" title="- YouTube" fullwidth="true"/> + + +Without your continued support and excitement for what we’re all creating, it wouldn’t have been possible to become one of the most vibrant global developer communities in the world. We can’t wait to see what you’ll build next. \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-2-10/images/0-XysmyfnVC_Rt_rj.webp b/sites/www/content/blog/whats-new-in-flutter-2-10/images/0-XysmyfnVC_Rt_rj.webp new file mode 100644 index 0000000000..a213129b66 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-10/images/0-XysmyfnVC_Rt_rj.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-10/images/004e4XmQgP9CAyJ6x.webp b/sites/www/content/blog/whats-new-in-flutter-2-10/images/004e4XmQgP9CAyJ6x.webp new file mode 100644 index 0000000000..cb46fbd1d9 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-10/images/004e4XmQgP9CAyJ6x.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-10/images/01U5AWT3j69NL5jWn.webp b/sites/www/content/blog/whats-new-in-flutter-2-10/images/01U5AWT3j69NL5jWn.webp new file mode 100644 index 0000000000..26dceb21e4 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-10/images/01U5AWT3j69NL5jWn.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-10/images/0278rYAB5Pac8_7SD.webp b/sites/www/content/blog/whats-new-in-flutter-2-10/images/0278rYAB5Pac8_7SD.webp new file mode 100644 index 0000000000..6f22126e7c Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-10/images/0278rYAB5Pac8_7SD.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-10/images/03bjgNV_6OjBYDj24.webp b/sites/www/content/blog/whats-new-in-flutter-2-10/images/03bjgNV_6OjBYDj24.webp new file mode 100644 index 0000000000..1cd2ff4f10 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-10/images/03bjgNV_6OjBYDj24.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-10/images/08k22xPfwMobXvYJB.webp b/sites/www/content/blog/whats-new-in-flutter-2-10/images/08k22xPfwMobXvYJB.webp new file mode 100644 index 0000000000..b39314b6ca Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-10/images/08k22xPfwMobXvYJB.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-10/images/0Tau4a2z5GHZzuJky.webp b/sites/www/content/blog/whats-new-in-flutter-2-10/images/0Tau4a2z5GHZzuJky.webp new file mode 100644 index 0000000000..7cdc22c2f3 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-10/images/0Tau4a2z5GHZzuJky.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-10/images/0bc2CaiWCLCpaYWMV.webp b/sites/www/content/blog/whats-new-in-flutter-2-10/images/0bc2CaiWCLCpaYWMV.webp new file mode 100644 index 0000000000..bb9fcdfcfb Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-10/images/0bc2CaiWCLCpaYWMV.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-10/images/0eA2UN8_kzzJreUQf.webp b/sites/www/content/blog/whats-new-in-flutter-2-10/images/0eA2UN8_kzzJreUQf.webp new file mode 100644 index 0000000000..53c99b0772 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-10/images/0eA2UN8_kzzJreUQf.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-10/images/0mWerp_uZBj64dBeC.webp b/sites/www/content/blog/whats-new-in-flutter-2-10/images/0mWerp_uZBj64dBeC.webp new file mode 100644 index 0000000000..f2ffbfba82 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-10/images/0mWerp_uZBj64dBeC.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-10/images/0qMbaTcooLzyIxwgp.webp b/sites/www/content/blog/whats-new-in-flutter-2-10/images/0qMbaTcooLzyIxwgp.webp new file mode 100644 index 0000000000..3348df8c3b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-10/images/0qMbaTcooLzyIxwgp.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-10/images/0sJIYBXONsWsYa5l4.webp b/sites/www/content/blog/whats-new-in-flutter-2-10/images/0sJIYBXONsWsYa5l4.webp new file mode 100644 index 0000000000..8785d367ea Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-10/images/0sJIYBXONsWsYa5l4.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-10/images/1VpOHaGxbrVr262jc6bMUmg.gif b/sites/www/content/blog/whats-new-in-flutter-2-10/images/1VpOHaGxbrVr262jc6bMUmg.gif new file mode 100644 index 0000000000..298a6366c1 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-10/images/1VpOHaGxbrVr262jc6bMUmg.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-10/index.md b/sites/www/content/blog/whats-new-in-flutter-2-10/index.md new file mode 100644 index 0000000000..e78080e9a8 --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-2-10/index.md @@ -0,0 +1,194 @@ +--- +title: "What’s New in Flutter 2.10" +description: "Windows stable, performance improvements, Material 3 updates and more!" +publishDate: 2022-02-03 +author: csells +image: images/0278rYAB5Pac8_7SD.webp +category: releases +layout: blog +--- + +I can’t believe it’s time again for a Flutter stable release! Hello and welcome to Flutter 2.10. It has been less than two months since our last release, but even in that short time we’ve had 1,843 issues closed and 1,525 PRs merged from 155 contributors worldwide. Thanks everyone for the great work, especially during the 2021 holiday season. + +We have several exciting things to announce as part of this release, including a big update on Flutter’s support for Windows, several significant performance improvements, new support for icons and colors in the framework, and some tooling improvements. In addition, we’ve got some updates on the removal of the dev channel, reduction in support for older versions of iOS, and a short list of breaking changes. Let’s get to it! + +## Ready for production apps on Windows + +First and foremost, the Flutter 2.10 release brings with it the stable release of Windows support. You no longer need to flip a flag to get the functionality that produces Windows apps on the stable channel of Flutter — now it’s available by default! + +<DashImage figure src="images/0278rYAB5Pac8_7SD.webp" /> + + +Of course, we did a lot more than just flip a flag. This release includes extensive improvements for text handling, keyboard handling, and keyboard shortcuts, as well as new integrations directly into Windows, with support for command-line arguments, globalized text entry, and accessibility. + +For much more on the Windows stable launch, see [the Announcing Flutter for Windows blog post](https://timsneath.medium.com/6979d0d01fed), which describes the architecture of Flutter on Windows that allows for deep integration and gives you a sense of just how many Flutter packages and plugins already support Windows. You can also see some examples of what our tooling and app partners are doing with Flutter on Windows! + +## Performance improvements + +This release of Flutter includes initial support for **dirty region management** provided by Flutter community member [knopp](https://github.com/knopp). He’s enabled [partial repaints for a single dirty region on iOS/Metal](https://github.com/flutter/engine/pull/28801). This change reduced 90th and 99th percentile rasterization times on a few benchmarks by an order of magnitude, and reduced GPU utilization on these benchmarks from more than 90% to less than 10%. + +<DashImage figure src="images/0mWerp_uZBj64dBeC.webp" /> + + +We expect to bring the benefits of partial repaints to [other platforms](https://github.com/flutter/engine/pull/29591) in future releases. + +In the Flutter 2.8 release, we [landed our own internal picture recording format](https://github.com/flutter/flutter/issues/53501). Now in Flutter 2.10, we’ve started building optimizations with it. As an example, one common case of **opacity layers is now [implemented much more efficiently](https://github.com/flutter/engine/pull/29775)**. Even in the worst case, frame raster times in our benchmarks fell to under a third of their previous value. + +<DashImage figure src="images/0sJIYBXONsWsYa5l4.webp" /> + + +We expect this optimization to expand to cover more cases as we continue developing the picture recording format. + +In profile and release modes, Dart code is compiled ahead of time to native machine code. The key to the efficiency and small size of this code is a whole program type flow analysis that unlocks many compiler optimizations and aggressive tree-shaking. However, as the type flow analysis must cover the whole program, it can be somewhat expensive. This release includes [**a faster implementation of type flow analysis**](https://dart.googlesource.com/sdk.git/+/e698500693603374ecc409e158f36c25bff45b12). Overall build time for the Flutter app in our benchmarks fell by ~10%. + +<DashImage figure src="images/03bjgNV_6OjBYDj24.webp" /> + + +As always, performance enhancements, reduced memory usage, and reduced latency are a priority for the Flutter team. Look forward to further improvements in future releases. + +## iOS updates + +In addition to performance improvements, we’ve also added some platform-specific features and enhancements. One new enhancement is [smoother keyboard animations in iOS](https://github.com/flutter/engine/pull/29281) from [luckysmg](https://github.com/luckysmg), which is provided automatically to your app without you having to do a thing. + +<DashImage figure src="images/0qMbaTcooLzyIxwgp.webp" /> + + +We’ve also improved the stability of the camera plugin for iOS by fixing a few [edge](https://github.com/flutter/plugins/pull/4608) [case](https://github.com/flutter/plugins/pull/4661) [crashes](https://github.com/flutter/plugins/pull/4619). + +Finally, **64-bit iOS architectures get a new feature to reduce memory usage: [compressed](https://github.com/flutter/engine/pull/30077) [pointers](https://github.com/flutter/engine/pull/30333)**. + +A 64-bit architecture represents pointers as a 8-byte data structure. When you have a lot of objects, the space taken up by the pointers themselves adds to the overall memory usage of your app, especially if you have larger, more complicated apps that have more GC churn. However, your iOS app is very unlikely to have enough objects to require a significant portion of even the 32-bit address space (2 billion objects), let alone the enormity of the 64-bit address space (9 million billion objects). + +Compressed pointers were provided in Dart 2.15 and in this release of Flutter, we use them to reduce the memory usage for 64-bit iOS apps. You can [check out the Dart 2.15 blog post for details](https://medium.com/dartlang/dart-2-15-7e7a598e508a). + +And while you’re reading Dart blog posts, don’t forget to [check out the Dart 2.16 announcement](https://medium.com/dartlang/dd87abd6bad1) for updates on supporting Flutter for Windows with package platform tagging and a new search experience on pub.dev. + +## Android updates + +This release contains a number of improvements for Android as well. By default, when you create a new app, **Flutter defaults to support the latest version of Android**, version 12 (API level 31). Also, in this release, **we’ve enabled [multidex](https://developer.android.com/studio/build/multidex) support automatically**. If your app supports Android SDK versions below 21, and it exceeds the 64K method limit, simply pass the `--multidex` flag to `flutter build appbundle` or `flutter build apk` and your app will support multidex. + +And last but not least, we listened to your feedback that Gradle error messages can be intimidating. For this reason, **the Flutter tool now suggests resolution steps to common issues**. For example, if you add a plugin to your app that requires you to increase the minimum supported Android SDK version, you now see a “Flutter Fix” suggestion in the logs. + +<DashImage figure src="images/01U5AWT3j69NL5jWn.webp" /> + + +We continue to add more suggestions to common error messages, and would love to get your feedback on other error messages where this treatment would be useful. + +## Web updates + +This release contains some improvements on the web as well. For example, in previous releases, when scrolling to the edge of a multiline `TextField` on the web, it wouldn’t scroll properly. This release introduces **[edge scrolling for text selection](https://github.com/flutter/flutter/pull/93170):** when the selection moves outside of the text field, the field scrolls to view the scroll extent. This new behavior is available for both web and desktop apps. + +<DashImage figure src="images/1VpOHaGxbrVr262jc6bMUmg.gif" /> + + +In addition, this release of Flutter includes another notable improvement in the web. We’re always looking to reduce the overhead of our mapping of Flutter to the web. In previous versions, every time we wanted to bring a native HTML widget into your Flutter app, we needed an overlay as part of our platform view support for the web. Each one of these overlays enables custom painting but represents a certain amount of overhead. If you have a large number of native HTML widgets in your app, such as links, that adds up to a lot of overhead. With this release, **we’ve created a new “non-painting platform view” for the web that essentially removes that overhead**. And we’ve [taken advantage of this optimization](https://github.com/flutter/plugins/pull/4578) in [the `Link` widget](https://pub.dev/documentation/url_launcher/latest/link/Link-class.html), which means if you have many links in your Flutter web app, they no longer represent any significant overhead at all. We’ll be applying this optimization to other widgets over time. + +## Material 3 + +This release is the beginning of the transition to Material 3, which includes the ability to [**generate an entire color scheme from a single seed color**](https://github.com/flutter/flutter/pull/93463). + +With any color, you can construct an instance of the new `ColorScheme` type: + +```dart +final lightScheme = ColorScheme.fromSeed(seedColor: Colors.green); +final darkScheme = ColorScheme.fromSeed(seedColor: Colors.green, brightness: Brightness.dark); +``` + +There’s also a new `colorSchemeSeed` parameter to the `ThemeData` factory constructor that allows you to generate the theme’s color scheme: + +```dart +final lightTheme = ThemeData(colorSchemeSeed: Colors.orange, …); +final darkTheme = ThemeData(colorSchemeSeed: Colors.orange, brightness: Brightness.dark, …); +``` + +In addition, this release includes **the `ThemeData.useMaterial3` flag, which switches components to the new Material 3 look**. + +Last but not least, **we’ve added [1,028 new Material icons](https://github.com/flutter/flutter/pull/95007)**. + +<DashImage figure src="images/0-XysmyfnVC_Rt_rj.webp" alt="*A tiny sample of the 1,028 new Material icons*" caption="*A tiny sample of the 1,028 new Material icons*" /> + + +Updates can be found in [the issue tracking our Material 3 work](https://github.com/flutter/flutter/issues/91605); feel free to leave your feedback. + +## Integration testing improvements + +In December of 2020, we announced a new way to [do end-to-end testing using the integration_test package](https://medium.com/flutter/updates-on-flutter-testing-f54aa9f74c7e). This new package replaced the flutter_driver package as the recommended way to do integration testing, providing new features, like Firebase Test Lab support as well as support for web and desktop. + +Since that time, we’ve made further improvements in the integration testing story, including **bundling the integration_test package into the Flutter SDK itself**, making it easier to integrate with your app. There’s also **a new [migration guide if you’re moving your existing flutter_driver tests to integration_test](https://docs.flutter.dev/testing/integration-tests/migration)**. + +<DashImage figure src="images/004e4XmQgP9CAyJ6x.webp" alt="*An example from the new flutter_driver to integration_test migration guide*" caption="*An example from the new flutter_driver to integration_test migration guide*" /> + + +Several of the [existing](https://docs.flutter.dev/testing/integration-tests) [docs](https://docs.flutter.dev/cookbook/testing/integration/profiling), [samples](https://github.com/flutter/samples/tree/master/testing_app) and [codelabs](https://codelabs.developers.google.com/codelabs/flutter-app-testing) have also been updated for integration_test. To get started, point your browser to the [Testing Flutter apps](https://docs.flutter.dev/testing) page on flutter.dev. If you aren’t already using integration_test on your Flutter apps, now’s the time to start! + +## Flutter DevTools + +We’ve done some work on Flutter DevTools in this release as well, including one ease-of-use feature if you use DevTools from the command line. Instead of using `pub global activate` to download and execute the latest version, you can now **simply use `dart devtools`** and get the version that is up-to-date with the version of Flutter that you’re using. + +We’ve also made a number [of](https://github.com/flutter/devtools/pull/3526) [usability](https://github.com/flutter/devtools/pull/3493) [updates](https://github.com/flutter/devtools/pull/3480), including [**improved support for inspecting large lists and maps in the Debugger variables pane**](https://github.com/flutter/devtools/pull/3497) (thanks to [elliette](https://github.com/elliette)). + +<DashImage figure src="images/0Tau4a2z5GHZzuJky.webp" /> + + +And finally, we’re about to release our **annual DevTools survey**! Please provide your feedback and help us improve your development experience. + +<DashImage figure src="images/0bc2CaiWCLCpaYWMV.webp" /> + + +This survey prompt will show up directly in DevTools sometime in mid-February. Thanks! + +## VSCode improvements + +The Visual Studio Code extension for Flutter has also gotten a number of enhancements, including **color previews in more places in your code** and [**a color picker that updates your code for you**](https://github.com/Dart-Code/Dart-Code/issues/3240). + +<DashImage figure src="images/0eA2UN8_kzzJreUQf.webp" /> + + +In addition, if you’d like to be a tester on prerelease versions of the Dart and Flutter extensions for VSCode, you can [switch to prerelease versions in your extension settings](https://github.com/Dart-Code/Dart-Code/issues/3729). + +<DashImage figure src="images/08k22xPfwMobXvYJB.webp" /> + + +You can read about the details of this update and more in [this post](https://groups.google.com/g/flutter-announce/c/lR-yn1s9HKk) on [the flutter-announce mailing list](https://groups.google.com/g/flutter-announce). + +* [VS Code extensions v3.32](https://groups.google.com/g/flutter-announce/c/lR-yn1s9HKk) + +* [VS Code extensions v3.34](https://groups.google.com/g/flutter-announce/c/lDSpaiWvUdI) + +## Removing the dev channel + +In [the Flutter 2.8 release](https://medium.com/flutter/whats-new-in-flutter-2-8-d085b763d181), we announced that we were doing the work to **remove the dev channel**, simplifying your choices, and removing engineering overhead. In this release, [we’ve completed that work](https://github.com/flutter/flutter/issues/94962), including: + +* Updated Flutter tool to help migrate developers off the dev channel + +* Updated wiki to reflect updated customer promises + +* Updated deprecation policy + +* Removed dev channel support from DartPad, pre-submit tests, and the website + +The dev channel has now been scrubbed away. If we missed a spot, please [let us know](http://github.com/flutter/flutter/issues). + +## Sunsetting Support for iOS 9.3.6 + +Due to decreased usage and increased difficulty in maintaining the target devices in our lab, we are **[moving support](http://flutter.dev/go/rfc-32-bit-ios-support) for iOS 9.3.6 [from the “Supported” tier to the “Best Effort” tier](https://docs.flutter.dev/development/tools/sdk/release-notes/supported-platforms)**. This means that support for iOS 9.3.6 and support for 32-bit iOS devices will be maintained only through coding practices, and ad-hoc and community testing. + +In the Q3, 2022 stable release, we expect to drop support for 32-bit iOS devices and iOS versions 9 and 10 from Flutter stable releases. This means that apps built against stable Flutter SDKs after that point will no longer work on 32-bit iOS devices, and the minimum iOS version supported by Flutter will increase to iOS 11. + +## Breaking Changes + +We also work to have **a small number of breaking changes** in each release and with this release, although we’re not quite at zero yet — but we’ll keep trying! + +* [Required Kotlin version](https://docs.flutter.dev/release/breaking-changes/kotlin-version) + +* [Deprecated API removed after v2.5](https://docs.flutter.dev/release/breaking-changes/2-5-deprecations) + +* [Raw images on Web uses correct origin and colors](https://docs.flutter.dev/release/breaking-changes/raw-images-on-web-uses-correct-origin-and-colors) + +* [Scribble Text Input Client](https://docs.flutter.dev/release/breaking-changes/scribble-text-input-client) + +If you’re still using these APIs, you can [read the migration guidance on flutter.dev](https://docs.flutter.dev/release/breaking-changes). As always, many thanks to the community for [contributing tests](https://github.com/flutter/tests/blob/master/README.md) that help us identify these breaking changes. + +## Summary + +From all of us here on the Flutter Team at Google, we want to say — thank you. Thank you for being part of the community that has helped Flutter become the most popular cross-platform UI toolkit, as measured by analysts like Statista and SlashData. Stable support for Windows is just the beginning of a packed year, and we can’t wait for everything we’ll build together! \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/00_8hDcyhjXasCJX3.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/00_8hDcyhjXasCJX3.webp new file mode 100644 index 0000000000..131b8295a4 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/00_8hDcyhjXasCJX3.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/00pqAAk_u0mKtRZx9.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/00pqAAk_u0mKtRZx9.webp new file mode 100644 index 0000000000..8250367b05 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/00pqAAk_u0mKtRZx9.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/058dlCP5YlIl5BdvR.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/058dlCP5YlIl5BdvR.webp new file mode 100644 index 0000000000..7f9848f98e Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/058dlCP5YlIl5BdvR.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/0C2RP0oCtMM9fvpgp.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0C2RP0oCtMM9fvpgp.webp new file mode 100644 index 0000000000..ba21ebc0bd Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0C2RP0oCtMM9fvpgp.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/0FcPA9ntTT4JPVo5U.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0FcPA9ntTT4JPVo5U.webp new file mode 100644 index 0000000000..655a38ff16 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0FcPA9ntTT4JPVo5U.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/0K7L6ppH1A1gBmVY-.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0K7L6ppH1A1gBmVY-.webp new file mode 100644 index 0000000000..b247d87561 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0K7L6ppH1A1gBmVY-.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/0NofBGAEBtDHtwclP.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0NofBGAEBtDHtwclP.webp new file mode 100644 index 0000000000..9b9f64c60f Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0NofBGAEBtDHtwclP.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/0RFbQWBEWCoP4PF1j.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0RFbQWBEWCoP4PF1j.webp new file mode 100644 index 0000000000..9749ff4347 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0RFbQWBEWCoP4PF1j.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/0ROXD5MqgkYuZgDZQ.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0ROXD5MqgkYuZgDZQ.webp new file mode 100644 index 0000000000..8b822474bc Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0ROXD5MqgkYuZgDZQ.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/0UgjcNnlHqFgKCH-t.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0UgjcNnlHqFgKCH-t.webp new file mode 100644 index 0000000000..8553f435ca Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0UgjcNnlHqFgKCH-t.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/0d2HU5GSLz88DZ7pv.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0d2HU5GSLz88DZ7pv.webp new file mode 100644 index 0000000000..38e77e632c Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0d2HU5GSLz88DZ7pv.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/0fS4WbRPwmo_FQgDo.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0fS4WbRPwmo_FQgDo.webp new file mode 100644 index 0000000000..7f8fd68050 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0fS4WbRPwmo_FQgDo.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/0hZswrrQ3ANrj6spe.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0hZswrrQ3ANrj6spe.webp new file mode 100644 index 0000000000..d5ce1ab4f6 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0hZswrrQ3ANrj6spe.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/0hlsSKR8Uco4AurMH.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0hlsSKR8Uco4AurMH.webp new file mode 100644 index 0000000000..25d2a22f84 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0hlsSKR8Uco4AurMH.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/0krVx4ycTN2nTl7wO.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0krVx4ycTN2nTl7wO.webp new file mode 100644 index 0000000000..0b3b6f0105 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0krVx4ycTN2nTl7wO.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/0mMd1Bs_OZWT7UB0l.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0mMd1Bs_OZWT7UB0l.webp new file mode 100644 index 0000000000..a7d1bc7f7c Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0mMd1Bs_OZWT7UB0l.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/0rMkCGirgD14ME-Na.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0rMkCGirgD14ME-Na.webp new file mode 100644 index 0000000000..121e069606 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0rMkCGirgD14ME-Na.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/0rn6BZioI2VeCqSQY.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0rn6BZioI2VeCqSQY.webp new file mode 100644 index 0000000000..c21d93caeb Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0rn6BZioI2VeCqSQY.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/0u6tJI44Ucu9fDvAb.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0u6tJI44Ucu9fDvAb.webp new file mode 100644 index 0000000000..1e87ebadfb Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0u6tJI44Ucu9fDvAb.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/0uLg1AGohi6Xzud9H.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0uLg1AGohi6Xzud9H.webp new file mode 100644 index 0000000000..7f89c894fa Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0uLg1AGohi6Xzud9H.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/images/0ymJePefMMrQBezVi.webp b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0ymJePefMMrQBezVi.webp new file mode 100644 index 0000000000..ddcf0fa099 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-2/images/0ymJePefMMrQBezVi.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-2/index.md b/sites/www/content/blog/whats-new-in-flutter-2-2/index.md new file mode 100644 index 0000000000..f5ab59ef2e --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-2-2/index.md @@ -0,0 +1,577 @@ +--- +title: "What’s new in Flutter 2.2" +description: "The Flutter 2.2 release focuses on polish and optimization, including iOS performance improvements, Android deferred components, updated…" +publishDate: 2021-05-19 +author: csells +image: images/0fS4WbRPwmo_FQgDo.webp +category: releases +layout: blog +--- + +*The Flutter 2.2 release focuses on polish and optimization, including iOS performance improvements, Android deferred components, updated service worker for Flutter web and more!* + +Today is the day we make Flutter 2.2 available. You get to it by switching to the stable channel and upgrading your current Flutter installation, or going to [flutter.dev/docs/get-started](http://flutter.dev/docs/get-started) to start a new installation. + +Even though it’s only been a couple of months since the Flutter 2 release, we have a lot of improvements to share in 2.2. This release merges 2,456 PRs and closes 3,105 issues across the framework, engine, and plugins repositories. Special shoutout to the Flutter community at large who provided a significant number of PRs and PR reviews, including [Abhishek01039](https://github.com/Abhishek01039) who contributed the most PRs (17) and [xu-baolin](https://github.com/xu-baolin), who reviewed the most PRs (9) towards Flutter 2.2. Thanks to all contributors for your help in bringing Flutter 2.2 to the stable channel. We couldn’t do it without you. + +With each new Flutter release to stable comes a new set of updates, whether those are performance enhancements, new features or bug fixes. In addition, a release includes a number of features that haven’t yet ready for production use but that we want you to be able to verify that they’re working the way you want them to. And finally, each new release comes with a set of associated tooling updates and updates from the larger Flutter community. To be honest, there is so much going on with each new release of Flutter these days that we can’t reasonably capture it all in a single blog post, but we’ll try to hit the highlights. + +## Flutter 2.2 updates in stable + +This release covers a wide range of improvements on top of Flutter 2, including updates across Android, iOS, and web, new Material icons, updates to text handling, scrollbar behavior, and mouse cursor support for the `TextSpan` widget and new guidance on how to best support multiple kinds of platforms from a single source code base. All of these features are available in stable now and available for your use in production apps. And all of them are built on a new release of Dart. + +### Dart 2.13 + +Flutter 2.2 comes with the Dart 2.13 release. Among other things, this Dart update contains a new type aliases feature, which enables you to create aliases for types as well as for functions: + +```dart +// Type alias for functions (existing) +typedef ValueChanged<T> = void Function(T value); + +// Type alias for classes (new!) +typedef StringList = List<String>; + +// Rename classes in a non-breaking way (new!) +@Deprecated("Use NewClassName instead") +typedef OldClassName<T> = NewClassName<T>; +``` + +Type aliases make it possible to give nice short names to long, complicated types, and it also lets you rename your classes in a non-breaking way. There’s more that’s new in Dart 2.13 as well; check out the details in [the Dart 2.13 release announcement](https://medium.com/dartlang/announcing-dart-2-13-c6d547b57067). + +### Flutter web updates + +Flutter’s newest stable platform, web, has been improved in this release. + +To start, we’ve optimized caching behavior with a new service worker-loading mechanism, and fixed double-downloading of `main.dart.js`. In previous versions of Flutter web, the service worker downloaded updates to your app in the background while giving your user access to the stale version of your app. Once that update was downloaded, the user wouldn’t see those changes until they refreshed the browser page a couple times. As of Flutter 2.2, when the new service worker detects a change, the user will wait until the updates are downloaded to use the app, but then they’ll see the updates without requiring a second manual refresh of the page. + +Enabling this change requires you to regenerate the `index.html` of your Flutter app. To do that, save your modifications, delete the `index.html` file, and then run `flutter create .` in your project directory to recreate it. + +We also made improvements to both web renderers. For HTML, we added support for [font features](https://developer.mozilla.org/en-US/docs/Web/CSS/font-feature-settings) to enable setting [`FontFeature`](https://api.flutter.dev/flutter/dart-ui/FontFeature-class.html) as well as using canvas APIs to render text so that it appears in the correct place when hovering. For both HTML and CanvasKit, we added support for shader masks and [`computeLineMetrics`](https://api.flutter.dev/flutter/painting/TextPainter/computeLineMetrics.html), addressing the parity gaps between Flutter web and mobile apps. For example, developers can now use [opacity masks](https://api.flutter.dev/flutter/widgets/Opacity-class.html) to perform fade-out transitions with shader masks, and use `computeLineMetrics` as they would for mobile apps. + +For Flutter web, as well as Flutter in general, accessibility is one of our top priorities. As designed, Flutter implements accessibility by building a `SemanticsNode` tree. Once a Flutter web app user enables accessibility, the framework generates a DOM tree parallel to the `RenderObject` DOM tree, and translates the semantic properties to Aira. In this release, we improved semantic node position to close the gap between mobile and desktop web apps when using transforms, which means that the focus box should appear properly over elements when widgets are styled with transforms. To see this in action, check out this video by Victor Tsaran, who leads the Accessibility program for Material Design, using VoiceOver with Flutter Gallery App: + +<YoutubeEmbed id="A6Sx0lBP8PI" title="Flutter's web support - Accessibility Demo" fullwidth="true"/> + + +We also exposed the semantics node debug tree with a command line flag in profile and release modes to help developers debug accessibility by visualizing the semantic nodes created for their web app. + +To enable this for your own Flutter web app, run the following: + +``` +$ flutter run -d chrome --profile \ + --dart-define=FLUTTER_WEB_DEBUG_SHOW_SEMANTICS=true +``` + + +With that flag activated, you’ll be able to see your semantic nodes on top of the widgets, which allows you to debug and see if semantic elements are placed where they shouldn’t be. If you find examples like that, please don’t hesitate to [file a bug report](https://goo.gle/flutter_web_issue). + +While we’ve made significant progress with support for a set of core accessibility features, we will continue to improve accessibility support. In builds available on the master and dev channels beyond the 2.2 stable release, we’ve added an API to let developers programmatically [auto-enable accessibility](https://github.com/flutter/engine/pull/25830) for their apps and are fixing issues with [using Tab with screen readers](https://github.com/flutter/engine/pull/25797). + +And last but certainly not least, the latest version of Flutter DevTools now supports the layout explorer for your Flutter web apps. + +<DashImage figure src="images/0fS4WbRPwmo_FQgDo.webp" /> + + +This update gives you the same layout debugging tool on the web that you’re used to with your mobile and desktop apps. + +### iOS page transitions and incremental installs + +For iOS, in this release [we’ve made the page transition smoother in Cupertino by reducing the time it takes to render the frames of the animation by 75%](https://github.com/flutter/flutter/pull/75670), and potentially more on low-end phones. We don’t just look for end-user performance improvements; we’re always looking for ways to improve the development performance as well. + +In this release, we’ve [implemented incremental iOS installs](https://github.com/flutter/flutter/pull/77756) during the development process. In our benchmarks, we’ve seen a 40% decrease in the amount of time to install an updated version of your iOS app, which decreases your turn-around time when testing app changes. + +### Use Flutter to build platform adaptive apps + +As Flutter expands to support more platforms in stable, it becomes useful to consider apps that support not just different form factors, such as mobile, tablet, and desktop, but also different input types (touch vs. mouse+keyboard) and platforms with different idioms, such as navigation drawers vs. system menus for navigation. We call apps that can adjust to the details of different target platforms to be “platform adaptive” apps. + +For an introduction to the considerations you’ll want to keep in mind when building platform adaptive apps, we’ll point you at the [Building platform adaptive apps](https://events.google.com/io/session/868dfd56-7f8c-49ee-84ad-ac69a23ba19d?lng=en) session from Kevin Moore. For a more detailed look, check out [the platform adaptive apps guidance on flutter.dev](https://flutter.dev/docs/development/ui/layout/building-adaptive-apps). + +And, finally, for sample apps that have been written for multiple platforms according to these principles, we recommend the [Flokk](https://flutter.gskinner.com/flokk) and [Flutter Folio](https://flutter.gskinner.com/folio) apps from gSkinner. You can download the code for [Flokk](https://github.com/gskinnerTeam/flokk) and [Folio](https://github.com/gskinnerTeam/flutter-folio) as well as download [Flokk](https://flutter.gskinner.com/flokk/#g-download) and [Folio](https://flutter.gskinner.com/folio/#g-download) from the various app stores or run them directly from your browser. Another great sample is the app used to create the guidance itself: + +<YoutubeEmbed id="8YUIrIGGc3Y" title="Flutter Adaptive Demo App" fullwidth="true"/> + + +The UX portions of the Flutter platform adaptive apps guidance is based on the new [Material Guidance for Large Screens](https://material.io/blog/material-design-for-large-screens). This new guidance from the Material team includes reworks of several of the main layout articles as well as updates to several components and an updated Design Kit, all with large screens in mind. + +<DashImage figure src="images/0ROXD5MqgkYuZgDZQ.webp" /> + + +Flutter’s goal has always been to enable apps that do more than just run on multiple platforms; we’re not done until your apps run *great* on all of the platforms you’re targeting. Flutter has the support you need to not only target your app at multiple platforms but also intends to *tailor* your apps for screen size, input modes, and idioms of each of these platforms. + +### More Material icons + +And on the subject of Material guidance, in this release we’ve landed not one but [two](https://github.com/flutter/flutter/pull/76607) [separate](https://github.com/flutter/flutter/pull/78311) PRs adding new Material icons to Flutter, including an icon for Dash herself! + +<DashImage figure src="images/0hlsSKR8Uco4AurMH.webp" /> + + +<DashImage figure src="images/0K7L6ppH1A1gBmVY-.webp" /> + + +These updates bring the total number of Material icons for your apps up to more than 7,000. If you have trouble finding the icon you’re looking for in that embarrassment of riches (who wouldn’t?) you can search by category and name at [fonts.google.com/icons](http://fonts.google.com/icons). + +<DashImage figure src="images/0NofBGAEBtDHtwclP.webp" alt="Searching for Flutter Material icons by name" caption="Searching for Flutter Material icons by name" /> + + +Once you find the perfect icon, the new ‘Flutter’ tab shows you how to use it, or you can download just that icon for use as a stand-alone asset in your app. Adding Dash to your Flutter app has never been easier. + +### Improved text handling + +As we continue to improve Flutter to support the specifics on each platform, we continue to push into new areas that weren’t as important on mobile form-factors as they are on desktop form-factors. One of those areas is text handling. In this release, we’ve started refactoring how we handle text input that enables features like canceling a keystroke as it bubbles through the widget hierarchy, and by introducing the ability to fully customize the keystrokes associated with text actions. + +Being able to cancel a keystroke allows Flutter to implement things like using the spacebar and the arrow keys without triggering scroll events, giving your end users a more intuitive experience. You can use this same capability to handle a keystroke before it gets to the parent widgets in your own apps. Another example, is that in this release you can Tab between the `TextField` and a button in your Flutter app and it just works: + +```dart +import 'package:flutter/material.dart'; + +void main() => runApp(App()); + +class App extends StatelessWidget { + @override + Widget build(BuildContext context) => MaterialApp( + title: 'Flutter Text Editing Fun', + home: HomePage(), + ); +} + +class HomePage extends StatelessWidget { + @override + Widget build(BuildContext context) => Scaffold( + body: Column( + children: [ + TextField(), + OutlinedButton(onPressed: () {}, child: const Text('Press Me')), + ], + ), + ); +} + +``` + +<DashImage figure src="images/0krVx4ycTN2nTl7wO.webp" alt="Flutter 2.2 can cancel a keystroke from bubbling up the widget hierarchy, for example allowing TAB to change focus from a TextField" caption="Flutter 2.2 can cancel a keystroke from bubbling up the widget hierarchy, for example allowing TAB to change focus from a TextField" /> + + +Custom text actions allow you to do things like special handling of the Enter key in a TextField; for example, you can trigger sending a message in a chat client while still allowing a newline to be inserted via Ctrl+Enter. These same text actions [allow Flutter itself to provide different keystrokes](https://github.com/flutter/flutter/pull/75032) to match the behavior of text editing to the host OS itself, for example, Ctrl+C on Windows and Linux, but Cmd+C on macOS. + +As an example of this, the following sample overrides the default left arrow action and provides a new action for the backspace and delete keys: + +```dart +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +void main() => runApp(MyApp()); + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) => MaterialApp( + title: 'Flutter TextField Key Binding Demo', + home: Scaffold(body: UnforgivingTextField()), + ); +} + +/// A text field that clears itself if the user tries to back up or correct +/// something. +class UnforgivingTextField extends StatefulWidget { + @override + State<UnforgivingTextField> createState() => _UnforgivingTextFieldState(); +} + +class _UnforgivingTextFieldState extends State<UnforgivingTextField> { + // The text editing controller used to clear the text field. + late TextEditingController controller; + + @override + void initState() { + super.initState(); + controller = TextEditingController(); + } + + @override + Widget build(BuildContext context) => Shortcuts( + shortcuts: <LogicalKeySet, Intent>{ + // This overrides the left arrow key binding that the text field normally + // has in order to move the cursor back by a character. The default is + // created by the MaterialApp, which has a DefaultTextEditingShortcuts + // widget in it. + LogicalKeySet(LogicalKeyboardKey.arrowLeft): const ClearIntent(), + + // This binds the delete and backspace keys to also clear the text field. + // You can bind any key, not just those already bound in + // DefaultTextEditingShortcuts. + LogicalKeySet(LogicalKeyboardKey.delete): const ClearIntent(), + LogicalKeySet(LogicalKeyboardKey.backspace): const ClearIntent(), + }, + child: Actions( + actions: <Type, Action<Intent>>{ + // This binds the intent that indicates clearing a text field to the + // action that does the clearing. + ClearIntent: ClearAction(controller: controller), + }, + child: Center(child: TextField(controller: controller)), + ), + ); +} + +/// An intent that is bound to ClearAction. +class ClearIntent extends Intent { + const ClearIntent(); +} + +/// An action that is bound to ClearIntent that clears the TextEditingController +/// passed to it. +class ClearAction extends Action<ClearIntent> { + ClearAction({required this.controller}); + + final TextEditingController controller; + + @override + Object? invoke(covariant ClearIntent intent) { + controller.clear(); + } +} +``` + +<DashImage figure src="images/0RFbQWBEWCoP4PF1j.webp" alt="Unforgiving TextField example where pressing left arrow or ESC clears the text" caption="Unforgiving TextField example where pressing left arrow or ESC clears the text" /> + + +We’ve still got more work to do, but we’re working to give you complete text editing actions. Our goal is that, by the time Flutter desktop gets to stable, your users won’t be able to tell the difference between editing text in their Flutter apps vs any other app on the host OS. + +### Automatic scrolling behavior + +As part of our continuing quest to make Flutter apps behave like the best apps on each platform, we took another look at scrollbars in this release. When it comes to actually showing a scrollbar, both Android and iOS are the same; they don’t show one by default. On the other hand, for a desktop app a scrollbar is usually shown automatically when the content is larger than the container, which requires you to add a `Scrollbar` parent widget. To get the correct behavior on mobile or desktop, this release automatically adds a `Scrollbar` when necessary. + +Consider the following Scrollbar-less code: + +```dart +import 'package:flutter/material.dart'; + +void main() => runApp(App()); + +class App extends StatelessWidget { + @override + Widget build(BuildContext context) => MaterialApp( + title: 'Automatic Scrollbars', + home: HomePage(), + ); +} + +class HomePage extends StatelessWidget { + @override + Widget build(BuildContext context) => Scaffold( + body: ListView.builder( + itemCount: 100, + itemBuilder: (context, index) => Text('Item $index'), + ), + ); +} + +``` + +When running it on the desktop, a scrollbar appears: + +<DashImage figure src="images/0ymJePefMMrQBezVi.webp" /> + + +If you don’t like the way the scrollbar looks or that it’s always shown, you can set a [`ScrollBarTheme`](https://api.flutter.dev/flutter/material/ThemeData/scrollbarTheme.html). If you don’t like this default behavior, you can change it app-wide or on a particular instance by setting a [`ScrollBehavior`](https://api.flutter.dev/flutter/widgets/ScrollBehavior-class.html). For more details about the new default scrollbar behavior and how to migrate your code to the new set of best practices, check out [the docs on flutter.dev](https://flutter.dev/docs/release/breaking-changes/default-desktop-scrollbars). + +### Mouse cursors over text spans + +In previous versions of Flutter, you could add a mouse cursor (like a hand indicating something clickable) over any widget. In fact, Flutter itself adds those mouse cursors for you in most cases, like adding a hand mouse cursor over all of the buttons. However, if you wanted a run of rich text with different text spans with their own styles and potentially long enough to wrap, you were out of luck — a `TextSpan` is not a `Widget` and therefore could not be used as a visual scope for a mouse cursor… until now! As of this release, when you have a `TextSpan` with a gesture recognizer, you will automatically get the corresponding mouse cursor: + +```dart +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart' as urlLauncher; + +void main() => runApp(App()); + +class App extends StatelessWidget { + static const title = 'Flutter App'; + @override + Widget build(BuildContext context) => MaterialApp( + title: title, + home: HomePage(), + ); +} + +class HomePage extends StatelessWidget { + @override + Widget build(BuildContext context) => Scaffold( + appBar: AppBar(title: Text(App.title)), + body: Center( + child: RichText( + text: TextSpan( + style: TextStyle(fontSize: 48), + children: [ + TextSpan( + text: 'This is not a link, ', + style: TextStyle(color: Colors.black), + ), + TextSpan( + text: 'but this is', + style: TextStyle(color: Colors.blue), + recognizer: TapGestureRecognizer() + ..onTap = () { + urlLauncher.launch('https://flutter.dev'); + }, + ), + ], + ), + ), + ), + ); +} + +``` + +Now you can have all of the wrapping text spans you want and any of them with recognizers will get the appropriate mouse cursors. + +<DashImage figure src="images/0u6tJI44Ucu9fDvAb.webp" /> + + +In this release, `TextSpan` also supports `onEnter` and `onExit` along with the `mouseCursor`. Things like this may seem small but they go a long way towards making a Flutter app feel just like a user expects it to feel. + +## Flutter 2.2 updates in preview + +In addition to the new features available for production use, Flutter 2.2 comes with a number of features in preview, including iOS shader compiler performance improvements, Android deferred component support, Flutter desktop updates, and ARM64 Linux host support from Sony. Please give these a try and [let us know if you have any issues](http://github.com/flutter/flutter/issues). + +### Preview: iOS shader compilation improvements + +In graphics rendering terms, a “shader” is a program to be compiled and run on the GPU available on the end-user’s device. Flutter has used shaders in the underlying Skia graphics library since its inception to provide native performance in its own high quality graphics effects with colors, shadows, animation, and so on. Due to the flexibility of Flutter’s APIs, shaders are generated and compiled just-in-time, synchronous with the frame workloads that need them. When the time to compile shaders goes beyond the frame budget, the result is noticeable to users as “jank.” + +To avoid jank, Flutter has provided the ability to cache shaders during a training run, which are then packaged and bundled with an app, and compiled before the first frame while the Flutter Engine is starting up. This means that the precompiled shaders don’t have to be compiled during a frame workload, and won’t cause jank. However, Skia initially only implemented this feature for OpenGL. + +So, when we enabled the Metal backend by default on iOS in response to Apple deprecating OpenGL, there was an increase in worst frame times as measured by our benchmarks, and an increase in user reports of jank. Our own measurements indicated that these reports were often due to increased shader compilation times, an increase in the number of shaders that Skia generated for the Metal backend, and the failure of compiled shaders to be cached across runs such that jank persisted beyond the first run of an app. + +Therefore, until now, the only way to avoid this jank on iOS was to simplify scenes and animations, which was not ideal. + +However, right now on the dev channel is a preview of [the new support in Skia for shader warm-up for Metal](https://github.com/flutter/flutter/issues/79298). Through Skia, Flutter now compiles the bundled shaders before the first frame workload begins. + +<DashImage figure src="images/00_8hDcyhjXasCJX3.webp" alt="Traces showing precompilation occuring during application launch" caption="Traces showing precompilation occuring during application launch" /> + + +This solution comes with some caveats, however: + +* Skia still generates more shaders for the Metal backend than for the OpenGL backend. + +* The final shader compilation to machine code still happens synchronously with the frame workload, but that’s faster than doing the entire shader generation and compilation as part of the frame render time. + +* The resulting machine code is cached after the first run of an app until the device is rebooted. + +If you’d like to take advantage of this new support in your apps, you can [follow along with the instructions on flutter.dev](https://flutter.dev/docs/perf/rendering/shader#how-to-use-sksl-warmup). + +We’re not done with this work, however. On both Android and iOS, this implementation has a few drawbacks: + +* The size of the deployed app is larger because it contains the bundled shaders. + +* App startup latency is longer because the bundled shaders need to be precompiled. + +* We aren’t happy with the developer experience that this implementation implies. + +We consider that last issue the most important one to fix. In particular, we view the process of performing training runs, and reasoning about the trade-offs imposed by app size and app startup latency to be too onerous. Therefore, we continue to investigate approaches to eliminate shader compilation jank, and all jank in general, that do not rely on this implementation. In particular, we’re working with the Skia team to reduce the number of shaders it generates in response to Flutter’s requests, as well as investigating how much Flutter might be implemented with a small set of statically defined shaders bundled with the Flutter Engine. + +You can follow along with [this project in the Flutter repo](https://github.com/flutter/flutter/projects/188) to see our progress. + +### Android deferred components + +For Android, this release uses Dart’s split AOT compilation feature to [allow Flutter apps to download modules containing ahead-of-time compiled code and assets at runtime](https://github.com/flutter/flutter/pull/76192). We call each of these installable splits a *deferred component*. By deferring the download of code and assets to only when needed, initial install size can be significantly reduced. For example, we implemented a version of Flutter Gallery with all studies and demos deferred and saw a 46% decrease in initial install size. + +<DashImage figure src="images/0uLg1AGohi6Xzud9H.webp" alt="Downloading the Crane study in the Flutter Gallery" caption="Downloading the Crane study in the Flutter Gallery" /> + + +When building with deferred components enabled, Dart compiles code imported exclusively with the `deferred` keyword into separate shared libraries that are packed into deferred components together with assets. + +Deferred components are currently available only on Android, and this feature is offered as an early preview. Learn how to implement deferred components in the new [Deferred components](https://flutter.dev/docs/perf/deferred-components) page on flutter.dev. This page also links to a page on the Flutter wiki that contains a deep dive on how this feature works. Please log issues on [the Flutter issue tracker](http://github.com/flutter/flutter/issues). + +### Flutter Windows UWP alpha + +Another update for Flutter in this release is for desktop lovers; support for Windows UWP has moved to alpha in the dev channel (beyond the stable 2.2 version). UWP allows you to take your Flutter apps to devices where standard Windows apps don’t run, including Xbox. To try it out, you first need to [set up the UWP prerequisites](https://flutter.dev/desktop#windows-uwp). Then, switch to the dev channel and enable UWP support: + +``` +*$ flutter channel dev +$ flutter upgrade +$ flutter config — enable-windows-uwp-desktop* +``` + + +Once enabled, creating a Flutter app includes a new a `winuwp` folder, which allows you to build and run your app in a UWP container: + +``` +*$ flutter create uwp_fun +$ cd uwp_fun +$ flutter pub get +$ flutter run -d winuwp* +``` + + +Because you’re building a Windows UWP app, which runs in a sandbox environment on Windows, you’ll need to punch a hole in the app’s firewall on localhost during development to enable things like hot reload and debugger breakpoints. You can do that with a `checknetisolation` command by following the instructions on [the Flutter desktop docs page](http://flutter.dev/desktop/#windows-uwp). Once you’ve done that, you can see your favorite Flutter app running as a UWP app on Windows. + +<DashImage figure src="images/0d2HU5GSLz88DZ7pv.webp" alt="Your favorite Flutter app running in a Windows UWP container" caption="Your favorite Flutter app running in a Windows UWP container" /> + + +Of course, you can run much more interesting UWP apps, like these Flutter apps running on an Xbox. + +<YoutubeEmbed id="s_zIzr60vMA" title="Flutter UWP on XBOX" fullwidth="true"/> + + +Special shout out to [clarkezone](https://github.com/clarkezone) who’s been working on this support for about as long as I’ve been on the Flutter team. For more details about the Windows UWP alpha, check out [flutter.dev/desktop/#windows-uwp](http://flutter.dev/desktop/#windows-uwp). + +### ARM64 Linux host support from Sony + +Another excellent effort by a Flutter community member-at-large is from [HidenoriMatsubayashi](https://github.com/HidenoriMatsubayashi), a software engineer at Sony, who has contributed [support for targeting ARM64 Linux](https://github.com/flutter/flutter/pull/61221). This PR enables you to build and run Flutter apps on ARM64 Linux machines. + +<DashImage figure src="images/0hZswrrQ3ANrj6spe.webp" alt="Your favorite Flutter app running on an ARM64 Linux machine" caption="Your favorite Flutter app running on an ARM64 Linux machine" /> + + +It’s exciting to see the Flutter community bringing Flutter to places that the team at Google could never have imagined. Keep up the good work, HidenoriMatsubayashi! + +## Flutter ecosystem and tooling updates + +The Flutter engine and framework are just a part of the overall experience. Updates to the packages ecosystem and tooling are equally important to the Flutter developer experience. And we’ve got some great updates in those areas to share. + +On the ecosystem side, we have a number of new Flutter Favorite packages to announce as well as several updates to FlutterFire, Flutter’s support for Firebase. Even better, FlutterFire has support for the new Firebase App Check preview so Flutter developers can take advantage of it on day one. + +On the tooling side, there are new updates to Flutter DevTools for optimizing your app’s memory footprint and a new tab for the provider package. There are notable updates to the IDE plugins for both VS Code and Android Studio/IntelliJ and, if you’re a content author targeting Flutter, there’s a whole new way to integrate DartPad into your writing. + +Last but not least, there’s a new low-code app design and building tool called FlutterFlow that targets Flutter and runs on the web, since it was itself built with Flutter. + +### Flutter Favorite updates + +As part of this release, the Flutter Ecosystem Committee has been working hard to certify 24 new Flutter Favorite packages, our largest expansion yet. The newly tagged Flutter Favorites include: + +* **[FlutterFire packages](http://firebase.flutter.dev) in production:** `cloud_firestore`, `cloud_functions`, `firebase_auth`, `firebase_core`, `firebase_crashlytics`, `firebase_messaging,` and `firebase_storage` + +* **[Flutter Community “plus” packages](http://plus.fluttercommunity.dev):** `android_alarm_manager_plus`, `android_intent_plus`, `battery_plus`, `connectivity_plus`, `device_info_plus`, `network_info_plus`, `package_info_plus`, `sensors_plus,` and `share_plus` + +* **[`googleapis`](https://pub.dev/packages/googleapis) package** + +* **[`win32`](https://pub.dev/packages/win32) package** + +* **[`intl`](https://pub.dev/packages/intl) and [`characters`](https://pub.dev/packages/characters) packages** + +* **[Sentry](https://pub.dev/packages/sentry_flutter) packages:** `sentry` and `sentry_flutter` + +* **[`infinite_scroll_pagination`](https://pub.dev/packages/infinite_scroll_pagination) and [`flutter_native_splash`](https://pub.dev/packages/flutter_native_splash) packages** + +All of these packages have been migrated to null safety and support Android, iOS, and web as appropriate. For example, `firebase_crashlytics` has no underlying SDK on the web, and `android_alarm_manager_plus` is specifically designed for Android. + +The [Flutter Community](https://github.com/fluttercommunity) “plus” packages provide a superset of the corresponding packages from the Flutter team. For example, [the battery package](https://pub.dev/packages/battery) has been provided by the Flutter team at Google since before the initial Flutter release and has been migrated to null safety, but is only supported on Android and iOS. The [Flutter Community `battery_plus` package](https://pub.dev/packages/battery_plus), on the other hand, supports all six Flutter platforms, including web, Windows, macOS, and Linux. The award of the Flutter Favorite awards for all 9 of the “plus” packages represents a big step forward in maturity for the Flutter community as a whole. Flutter is much, much larger than just what the team of engineers at Google are doing. You should migrate your code to the “plus” packages soon and, in the coming weeks, the corresponding packages from Google will be updated to recommend that you do so. + +The `googleapis` plugin provides auto-generated Dart wrappers around 185 Google APIs for use in your client or server-side Dart applications (including your Flutter apps). If you want to learn more about this package, the author has an [I/O talk on powering up your Flutter apps using Google APIs](https://events.google.com/io/session/7f706716-0de0-4a9e-bad3-581afe8ef360). + +The win32 package is a marvel of engineering, wrapping most of the commonly used Win32 API calls using [Dart FFI](https://dart.dev/guides/libraries/c-interop) to make them accessible to Dart code without requiring a C compiler or the Windows SDK. As Flutter gains popularity on the Windows platform, the `win32` package has become a key dependency for many popular plugins, including [`path_provider`](https://pub.dev/packages/path_provider), one of the most popular. And as a test of completeness, the author [timsneath](https://github.com/timsneath) has done crazy things like implementing [notepad](https://github.com/timsneath/win32/tree/main/example/notepad), [snake](https://github.com/timsneath/win32/blob/main/example/snake.dart) and [tetris](https://github.com/timsneath/win32/tree/main/example/tetris), all in raw Win32 using raw Dart. + +<DashImage figure src="images/0rMkCGirgD14ME-Na.webp" alt="Tetris running on Windows built using Dart FFI and Win32 calls only" caption="Tetris running on Windows built using Dart FFI and Win32 calls only" /> + + +The `win32` package is definitely worth checking out if you do anything with Dart or Flutter on Windows. + +### FlutterFire updates and Firebase App Check + +FlutterFire, Flutter’s support for Firebase, is one of the most popular collections of plugins to use with Flutter. Invertase has done a tremendous job getting it to production for the Flutter 2 release and continuing to improve it since then. In fact, since that initial production release of FlutterFire, Invertase has reduced the number of open issues by 79% and reduced the number of outstanding PRs by 88%. Furthermore, not only have they done an excellent job with the production quality plugins, they’ve also migrated the beta quality plugins to null safety and kept them building and running on the same core so that you can mix and match. + +In addition, Invertase continues to add new features to the FlutterFire plugins, including a number of updates to Flutter’s integration with Cloud Firebase with this release of Flutter: + +* [`Typesafe` API](https://firebase.flutter.dev/docs/firestore/usage/#typing-collectionreference-and-documentreference) for reading and writing data + +* Support for the [Firebase Local Emulator Suite](https://firebase.flutter.dev/docs/storage/usage/#emulator-usage) + +* Optimizing your data queries with [data bundles](https://firebase.flutter.dev/docs/firestore/usage#data-bundles) + +And last, but not least, FlutterFire provides support for the beta release of a new Firebase product: [Firebase App Check](https://firebase.google.com/docs/app-check). Firebase App Check protects your backend resources like Cloud Storage from abuse like billing fraud or phishing. With App Check, devices running your Flutter app use an app identity attestation provider to certify that it is indeed your authentic app, and may also check that it’s running on an authentic, untampered device. This certification is attached to every request your app makes to your Firebase backend resources once you activate App Check. To learn more, see the [FlutterFire App Check documentation](https://firebase.flutter.dev/docs/app-check/overview). + +### Flutter DevTools updates + +Flutter DevTools comes with a number of notable updates with this release, including two memory tracking improvements and a whole new tab just for the `provider` plugin. + +The first memory tracking improvement in this release of DevTools provides the ability to track down where an object was allocated. This is handy to find the location in your code of a memory leak. + +<DashImage figure src="images/0mMd1Bs_OZWT7UB0l.webp" alt="Flutter DevTools memory tab allocation stack trace" caption="Flutter DevTools memory tab allocation stack trace" /> + + +The second is the ability to inject custom messages into the memory timeline. This allows you to provide markers specific to your app, like before and after you’ve done some memory intensive work so that you can check that you’re cleaning things up properly. + +<DashImage figure src="images/0FcPA9ntTT4JPVo5U.webp" alt="Flutter DevTools timeline tab custom memory events" caption="Flutter DevTools timeline tab custom memory events" /> + + +As Flutter apps in the wild get larger and larger, we will continue to ensure that Flutter developers have the tools they need to track down and fix memory leaks and runtime issues of all kinds. + +It isn’t just runtime issues in your use of the Flutter framework you want to track down; sometimes you want to track down issues related to packages as well. With over 15,000 Flutter-compatible packages and plugins on pub.dev, this becomes increasingly likely as your apps use more packages over time. So, with that in mind, we’ve been experimenting with adding a new **Provider** tab to Flutter DevTools. In fact, this tab was built by [Remi Roussel](https://github.com/rrousselGit), the author of [the `provider` package](http://pub.dev/packages/provider) itself (among many other wonderful things). If you’re running the latest version of Flutter DevTools and you are debugging a Flutter app that uses the `provider` plugin, you’ll automatically get the new **Provider** tab. + +<DashImage figure src="images/0UgjcNnlHqFgKCH-t.webp" alt="Flutter DevTools Provider tab in action" caption="Flutter DevTools Provider tab in action" /> + + +The Provider tab shows you the data associated with each of your providers, including real-time changes as you run your app. And as if that weren’t amazing enough, it allows you to change the data directly as a way to test the corner cases of your app! + +Working with Remi on this tab has taught us a few things about how to better support other package authors who would like to do the same thing; you can [read about how Remi built the Provider tab](https://invertase.io/blog/how-to-flutter-devtool-plugin) and our current thinking about how to enable more tabs in the [Flutter DevTools Plugins](https://docs.google.com/document/d/1BWX8YQ962Vsx-EUDuDHRG7RX94fJlZSDRu299YRwasE/) proposal. Please give us your feedback and feel free to reach out to tell us about your plans for a new tab in Flutter DevTools. + +This is only a few of the cool new things in Flutter DevTools in this release. For the complete list, check out the individual announcements here: + +* [Flutter DevTools 2.1 Release Notes](https://groups.google.com/g/flutter-announce/c/tCreMfJaJFU/m/38p1BBeiCAAJ) + +* [Flutter DevTools 2.2.1 Release Notes](https://groups.google.com/g/flutter-announce/c/t8opLnUyiFQ/m/dJth-jKxAAAJ) + +* [Flutter DevTools 2.2.3 Release Notes](https://groups.google.com/g/flutter-announce/c/t8opLnUyiFQ/m/YX5Ds_q0AgAJ) + +### IDE plugins updates + +Both the Visual Studio Code and the IntelliJ/Android Studio IDE extensions for Flutter were updated in this release as well. For example, the Visual Studio Code extension now supports two additional Dart code refactors: Inline Method and Inline Local Variable. + +<DashImage figure src="images/0C2RP0oCtMM9fvpgp.webp" alt="The new Dart refactor Inline Method in action" caption="The new Dart refactor Inline Method in action" /> + + +In the Android Studio/IntelliJ extension, we’ve added the ability to print all stack traces to the console with an option. + +<DashImage figure src="images/00pqAAk_u0mKtRZx9.webp" alt="You can now get all of the stack traces and not just the first one" caption="You can now get all of the stack traces and not just the first one" /> + + +This is helpful in projects where the root cause might be in a different package, which previously wasn’t being printed. We already have ideas on how to make this less obviously verbose, so look for more changes in the future. + +For the full list of changes to the IDE extensions for this release, check out these announcements: + +* [VS Code extension v3.21](https://groups.google.com/g/flutter-announce/c/gNtKp9c1glU/m/SZYTuwcQBwAJ) + +* [VS Code extension v3.22](https://groups.google.com/g/flutter-announce/c/1XR7baYZOVI/m/y6MGYrGhAAAJ) + +* [Flutter IntelliJ Plugin M55 Release](https://groups.google.com/g/flutter-announce/c/tYwFDPAtLu0/m/FrsntcNNBwAJ) + +* [Flutter IntelliJ Plugin M56 Release](https://groups.google.com/g/flutter-announce/c/EkgiAO4p3UM/m/P32ZXXKfAAAJ) + +### DartPad workshops + +To ensure that we have documentation ready across a rapidly growing Flutter developer community, the Dart and Flutter teams are always looking at ways to improve and expand the ways to create educational content. With this release, we’re adding a new, step-by-step UI for DartPad that devs can use to follow along with instructor-led workshops. + +<DashImage figure src="images/058dlCP5YlIl5BdvR.webp" alt="A DartPad workshop in action" caption="A DartPad workshop in action" /> + + +By adding instructions directly to DartPad, we’re enabling [a guided workshop experience for I/O](https://events.google.com/io/program/content?4=topic_flutter&5=type_workshop). However, we didn’t just build it for our own workshops; if you’d like to use it in your Dart or Flutter workshops, you can do so by following [the DartPad Workshop Authoring Guide](https://github.com/dart-lang/dart-pad/wiki/Workshop-Authoring-Guide). This is in addition to being able to [share code using DartPad in a Gist](https://github.com/dart-lang/dart-pad/wiki/Sharing-Guide) and [embedding DartPad in your own site](https://github.com/dart-lang/dart-pad/wiki/Embedding-Guide), which have been available for awhile now. + +We want everyone producing Dart and Flutter content to be able to provide rich, interactive experiences for their users. Please give this new feature a try and [let us know what you think](https://github.com/dart-lang/dart-pad/issues)! + +### Community Spotlight: FlutterFlow + +FlutterFlow is a “low code” app design and development tool for building apps all within your browser. It provides a WYSIWYG environment for laying out your app across multiple pages using real data from Firebase. The goal of a low code tool is to do most of the common things easily, allowing you to write as few lines of custom code as possible. In fact, as a demo, they built an entire multi-page mobile app for browsing the Metropolitan Museum of Art with zero code in less than an hour. You can see the entire process on YouTube. + +<YoutubeEmbed id="TXsjnd_4SBo" title="Building FlutterMet" fullwidth="true"/> + + +FlutterFlow outputs Flutter code, so if you need to add code to further customize your app, you can. You can read about [the FlutterFlow product launch on flutterflow.io](https://flutterflow.io/blog/launch). + +## Breaking Changes + +As always, we strive to reduce the number of breaking changes and in this release we’ve been able to limit it to removing these deprecations: + +* [73750](https://github.com/flutter/flutter/pull/73750) Remove deprecated BinaryMessages + +* [73751](https://github.com/flutter/flutter/pull/73751) Remove deprecated TypeMatcher class + +You can [find mitigations for these breaking changes on flutter.dev](https://flutter.dev/docs/release/breaking-changes/1-22-deprecations). + +## Summary + +As always, from all of us here on the Flutter Team at Google, we want to say — thank you. Thank you for being part of the community that makes all of this possible. With more than one in eight of new apps in the Play Store being built with Flutter and over 200,000 Flutter apps in the Play Store alone, our continued growth is mind blowing. Apps of all sizes around the world are entrusting their UI to Flutter to craft beautiful multi platform experiences to meet users wherever they may be. + +<DashImage figure src="images/0rn6BZioI2VeCqSQY.webp" /> + + +Finally, in case you missed it, before you leave I/O this year, don’t forget to check out the [I/O Photo Booth](https://photobooth.flutter.dev/#/) web app built in Flutter & Firebase to grab a selfie with Dash. We [open sourced the code](https://github.com/flutter/photobooth) so you can dig into best practices for Flutter web, camera plugin web support, and learn how we used cloud functions to generate custom social posts. \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0-5pu9nXQw88jixos.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0-5pu9nXQw88jixos.webp new file mode 100644 index 0000000000..98bc8a022b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0-5pu9nXQw88jixos.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/011VHtSVUTukATeQN.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/011VHtSVUTukATeQN.webp new file mode 100644 index 0000000000..a6b2abd923 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/011VHtSVUTukATeQN.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/03utkTiVxsZOcvcKl.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/03utkTiVxsZOcvcKl.webp new file mode 100644 index 0000000000..95dbddfb9e Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/03utkTiVxsZOcvcKl.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/04O_Zg7UAwbnGissJ.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/04O_Zg7UAwbnGissJ.webp new file mode 100644 index 0000000000..feb0bc2244 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/04O_Zg7UAwbnGissJ.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/07ivd_3xtI3uaZ4nO.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/07ivd_3xtI3uaZ4nO.webp new file mode 100644 index 0000000000..75f0b18928 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/07ivd_3xtI3uaZ4nO.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/08evmW5s37nGUIc98.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/08evmW5s37nGUIc98.webp new file mode 100644 index 0000000000..ddb4010d09 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/08evmW5s37nGUIc98.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0CNbVsc26rivkp2vI.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0CNbVsc26rivkp2vI.webp new file mode 100644 index 0000000000..d1c02569a7 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0CNbVsc26rivkp2vI.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0CfaqXymLvWZc6Cqo.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0CfaqXymLvWZc6Cqo.webp new file mode 100644 index 0000000000..4ac1bc556b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0CfaqXymLvWZc6Cqo.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0G5M8qrjxw3gyeT7a.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0G5M8qrjxw3gyeT7a.webp new file mode 100644 index 0000000000..353ebbaa62 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0G5M8qrjxw3gyeT7a.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0GTrbuGlv9G-YPvOi.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0GTrbuGlv9G-YPvOi.webp new file mode 100644 index 0000000000..bf946375fe Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0GTrbuGlv9G-YPvOi.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0IzjrKssfjooCGzKm.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0IzjrKssfjooCGzKm.webp new file mode 100644 index 0000000000..d5d5e7803b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0IzjrKssfjooCGzKm.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0OJrGTdsznMa2N0DI.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0OJrGTdsznMa2N0DI.webp new file mode 100644 index 0000000000..705a6956d8 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0OJrGTdsznMa2N0DI.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0S8Nl6GbXHBHV64Rr.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0S8Nl6GbXHBHV64Rr.webp new file mode 100644 index 0000000000..74d38859d6 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0S8Nl6GbXHBHV64Rr.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0TK2px5OYQ_ggn2jx.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0TK2px5OYQ_ggn2jx.webp new file mode 100644 index 0000000000..54c79eea26 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0TK2px5OYQ_ggn2jx.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0TvARS8SsME4sl-Ib.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0TvARS8SsME4sl-Ib.webp new file mode 100644 index 0000000000..d878a287c7 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0TvARS8SsME4sl-Ib.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0WnLtflm5SF1I9GxK.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0WnLtflm5SF1I9GxK.webp new file mode 100644 index 0000000000..4b181e5210 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0WnLtflm5SF1I9GxK.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0cLXjl-F3Vj0bFkKg.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0cLXjl-F3Vj0bFkKg.webp new file mode 100644 index 0000000000..ba9a51d6f0 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0cLXjl-F3Vj0bFkKg.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0chKF4N2FzPBN3R3G.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0chKF4N2FzPBN3R3G.webp new file mode 100644 index 0000000000..ca60932118 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0chKF4N2FzPBN3R3G.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0dzvJ63ojTXw-Grsu.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0dzvJ63ojTXw-Grsu.webp new file mode 100644 index 0000000000..e7b79c0365 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0dzvJ63ojTXw-Grsu.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0esYsdKzKL64130ln.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0esYsdKzKL64130ln.webp new file mode 100644 index 0000000000..f9069ab893 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0esYsdKzKL64130ln.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0ewigNSGco1ozScc0.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0ewigNSGco1ozScc0.webp new file mode 100644 index 0000000000..dbf6d865a1 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0ewigNSGco1ozScc0.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0iN9uTsyfsUOt8cTw.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0iN9uTsyfsUOt8cTw.webp new file mode 100644 index 0000000000..1f70aee1ae Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0iN9uTsyfsUOt8cTw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0kTKO_OcWee8OS0Lo.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0kTKO_OcWee8OS0Lo.webp new file mode 100644 index 0000000000..94b3be5203 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0kTKO_OcWee8OS0Lo.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0l8XoqqLcz6W0CkRE.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0l8XoqqLcz6W0CkRE.webp new file mode 100644 index 0000000000..421c2773cb Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0l8XoqqLcz6W0CkRE.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0rvrCGEZ-vkRxga-n.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0rvrCGEZ-vkRxga-n.webp new file mode 100644 index 0000000000..638ca51f6b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0rvrCGEZ-vkRxga-n.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0snvlKGuZPa9zHFBc.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0snvlKGuZPa9zHFBc.webp new file mode 100644 index 0000000000..20360f0614 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0snvlKGuZPa9zHFBc.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/0vxdeNoUw3boFaeFu.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0vxdeNoUw3boFaeFu.webp new file mode 100644 index 0000000000..eb5f58bde0 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/0vxdeNoUw3boFaeFu.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/1YX-xNXUTKnNuZNBAmt7CAQ.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/1YX-xNXUTKnNuZNBAmt7CAQ.webp new file mode 100644 index 0000000000..a0e7442570 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/1YX-xNXUTKnNuZNBAmt7CAQ.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/images/1uTH_ZL2MFJSlPvhuZ81o5A.webp b/sites/www/content/blog/whats-new-in-flutter-2-5/images/1uTH_ZL2MFJSlPvhuZ81o5A.webp new file mode 100644 index 0000000000..e39986cd69 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-5/images/1uTH_ZL2MFJSlPvhuZ81o5A.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-5/index.md b/sites/www/content/blog/whats-new-in-flutter-2-5/index.md new file mode 100644 index 0000000000..83b05edbc3 --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-2-5/index.md @@ -0,0 +1,368 @@ +--- +title: "What’s new in Flutter 2.5" +description: "Performance improvements, DevTools updates, new Material You support, a new app template, and more!" +publishDate: 2021-09-08 +author: csells +image: images/0esYsdKzKL64130ln.webp +category: releases +layout: blog +--- + +Hello and welcome to Flutter 2.5! This is a big release, with the 2nd highest stats in the history of Flutter releases: 4600 issues closed and 3932 PRs merged from 252 contributors with 216 reviewers. If we look back over the last year, we see a huge 21,072 PRs created by 1337 contributors, of which 15,172 of them were merged. While the “what’s new in Flutter” blog posts focuses on new features, our #1 job with Flutter is always making sure you have the features you need at the highest possible quality level. + +And in fact, this release continues a number of important performance and tooling improvements to track down performance problems in your own app. At the same time, there are a number of new features, including full screen support for Android, more Material You (also called v3) support, updated text editing to support switchable keyboard shortcuts, a new, more detailed look at your widgets in the Widget Inspector, new support for adding dependencies in your Visual Studio Code projects, new support for getting coverage information from your test runs in IntelliJ/Android Studio and a whole new app template to serve as a better foundation for your real-world Flutter apps. This release is jam-packed with exciting new updates, so let’s get started. + +## Performance: iOS shader warmup, async tasks, GC & message passing + +This release comes with several performance improvements. First on this list is a PR to wire up Metal shader precompilation from offline training runs ([#25644](https://github.com/flutter/engine/pull/25644)), which (as our benchmarks show) cuts worst case frame rasterization times by 2/3s, and the 99th percentile frame by half. We continue to make progress on reducing iOS jank and this is another step along that path. Shader warmup is but one source of jank, however. Previously, processing asynchronous events from the network, filesystem, plugins, or other isolates could interrupt animations, another source of jank. Following improvements to scheduling policies ([#25789](https://github.com/flutter/engine/pull/25789)) on the UI isolate’s event loop in this release, frame processing now takes priority over processing other asynchronous events, eliminating jank from this source in our testing. + +<DashImage figure src="images/04O_Zg7UAwbnGissJ.webp" alt="*Frame lag due to processing asynchronous event results before and after*" caption="*Frame lag due to processing asynchronous event results before and after*" /> + + +Another cause of jank is when the garbage collector (GC)pauses the UI thread to reclaim memory. Previously, memory for some images would only be reclaimed lazily in response to GC performed by the Dart VM. As a workaround in earlier releases, the Flutter Engine would hint to the Dart VM that image memory could be reclaimed by a GC, which in theory could lead to more timely memory reclamation. Unfortunately, in practice this led to too many major GCs, and the memory would still sometimes not be reclaimed quickly enough to avoid low-memory situations on memory constrained devices. In this release, memory for unused images is reclaimed eagerly ([#26219](https://github.com/flutter/engine/pull/26219), [#82883](https://github.com/flutter/flutter/pull/82883), [#84740](https://github.com/flutter/flutter/pull/84740)), reducing GCs considerably. + +<DashImage figure src="images/0OJrGTdsznMa2N0DI.webp" alt="*GCs before and after adding the fix to eagerly reclaim unused large image memory*" caption="*GCs before and after adding the fix to eagerly reclaim unused large image memory*" /> + + +For example, in one of our tests, playing a 20 second animated GIF went from needing 400+ GCs to needing just 4. Fewer major GCs means that animations involving images appearing and disappearing will have less jank, and consume less CPU and power. + +Another performance improvement in Flutter 2.5 is the latency when sending messages between Dart and Objective-C/Swift (iOS) or Dart and Java/Kotlin (Android). As part of [tuning-up](https://docs.google.com/document/d/1oNLxJr_ZqjENVhF94-PqxsGPx0qGXx-pRJxXL6LSagc/edit#heading=h.9gabvat7tlxf) message channels generally, removing unnecessary copies from messaging codecs reduced latencies by up to 50% depending on message size and device ([#25988](https://github.com/flutter/engine/pull/25988), [#26331](https://github.com/flutter/engine/pull/26331)). + +<DashImage figure src="images/0TvARS8SsME4sl-Ib.webp" alt="*iOS message latencies before and after*" caption="*iOS message latencies before and after*" /> + + +You can read more about the details of this work in the [Improving Platform Channel Performance in Flutter](https://medium.com/flutter/improving-platform-channel-performance-in-flutter-e5b4e5df04af) blog post by Aaron Clarke. + +One final performance update if you’re targeting iOS: In this release, Flutter apps built on on Apple Silicon M1 Macs run natively on ARM iOS simulators ([#pull/85642](https://github.com/flutter/flutter/pull/85642)). This means there’s no Rosetta translation between the Intel x86_64 instructions and ARM, which increases performance during your iOS app testing and allows you to avoid some subtle Rosetta issues ([#74970](https://github.com/flutter/flutter/issues/74970#issuecomment-858170914), [#79641](https://github.com/flutter/flutter/issues/79641)). This is another step along the path for full support in Flutter for Apple Silicon. Stay tuned for more. + +## Dart 2.14: formatting, language features, pub & linting out-of-the-box + +Of course, Flutter isn’t Flutter without the Dart language and runtime on which it builds. This release of Flutter comes with Dart 2.14. [The new release of Dart ](https://medium.com/@mit.mit/announcing-dart-2-14-b48b9bb2fb67)comes with new formatting to make [cascades](https://dart.dev/guides/language/language-tour#cascade-notation) more clear, new pub support for ignoring files, and new language features, including the return of the legendary triple shift operator. In addition, and one of the best things about Dart 2.14, is that this release has created a standard set of lints shared between new Dart and Flutter projects that come right out of the box. + +<DashImage figure src="images/0CNbVsc26rivkp2vI.webp" alt="*`flutter create` comes out of the box with an analysis_options.yaml file pre-populated with recommended Flutter lints*" caption="*`flutter create` comes out of the box with an analysis_options.yaml file pre-populated with recommended Flutter lints*" /> + + +Not only do you get these lints when you create a new Dart or Flutter project, but with [just a few steps](https://flutter.dev/docs/release/breaking-changes/flutter-lints-package#migration-guide), you can add this same analysis to your existing apps as well. For the details of these lints, the new language features and more, check out [the release announcement for Dart 2.14](https://medium.com/dartlang/announcing-dart-2-13-c6d547b57067). + +## Framework: Android full screen, Material You & text editing shortcuts + +The Flutter 2.5 release includes a number of fixes and improvements to the framework. Starting with [Android, we’ve fixed a set of related issues around full screen modes](https://github.com/flutter/flutter/pull/81303) with nearly 100 thumbs up between them. The names of the modes themselves makes this one of our favorite new features: *lean back, sticky, sticky immersive, and edge to edge*. This change also added a way to listen to fullscreen changes in the other modes. For example, if the user engages with the app, when the system UI comes back, developers can now write their code to return to fullscreen or do something else. + +<DashImage figure src="images/0-5pu9nXQw88jixos.webp" alt="*New Android edge-to-edge mode: normal mode (left), Edge to Edge mode (center), Edge to Edge with a custom SystemUIOverlayStyle (right)*" caption="*New Android edge-to-edge mode: normal mode (left), Edge to Edge mode (center), Edge to Edge with a custom SystemUIOverlayStyle (right)*" /> + + +In this release, we continue to build support for the new Material You (aka v3) specification, including updates to floating action button sizes and theming ([#86441](https://github.com/flutter/flutter/pull/86441)), and a new `MaterialState.scrolledUnder` state that you can see in action with the sample code in the PR ([#79999](https://github.com/flutter/flutter/pull/79999)). + +<DashImage figure src="images/07ivd_3xtI3uaZ4nO.webp" alt="*New Material You FAB sizes*" caption="*New Material You FAB sizes*" /> + + +<DashImage figure src="images/1uTH_ZL2MFJSlPvhuZ81o5A.webp" alt="*New MaterialState.scrolledUnder state in action*" caption="*New MaterialState.scrolledUnder state in action*" /> + + +And while we’re talking about scrolling, another improvement is the addition of scroll metrics notifications ([#85221](https://github.com/flutter/flutter/pull/85221), [#85499](https://github.com/flutter/flutter/pull/85499)), which provide notifications of scrollable areas even if the user isn’t scrolling. For example, the following shows the scrollbar appearing or disappearing as appropriate based on the underlying size of the `ListView`: + +<DashImage figure src="images/011VHtSVUTukATeQN.webp" alt="*The new scroll metrics notifications enabling the scrollbar to appear and disappear automatically without scrolling*" caption="*The new scroll metrics notifications enabling the scrollbar to appear and disappear automatically without scrolling*" /> + + +In this case, you don’t have to write any code but if you want to capture the [`ScrollMetricNotification`](https://master-api.flutter.dev/flutter/widgets/ScrollMetricsNotification-class.html)changes, you can. Special thanks goes out to community contributor [xu-baolin](https://github.com/xu-baolin) who worked hard on this and came up with a great solution. + +Another excellent contribution from the community is the addition of Material banner support to the `ScaffoldMessenger`. You may remember the `ScaffoldMessenger` from [the Flutter 2.0 release announcement](https://medium.com/flutter/whats-new-in-flutter-2-0-fe8e95ecc65) as a more robust way to show `SnackBars` at the bottom of the screen to provide users with notifications. In Flutter 2.5, you can now add a banner to the top of your scaffold that stays in place until the user dismisses it. + +<DashImage figure src="images/0WnLtflm5SF1I9GxK.webp" /> + + +Your app can get this behavior by calling the `showMaterialBanner` method of `ScaffoldMessenger`: + +```dart +class HomePage extends StatelessWidget { + const HomePage({Key? key}) : super(key: key); + @override + Widget build(BuildContext context) => Scaffold( + appBar: AppBar( + title: const Text('The MaterialBanner is below'), + ), + body: Center( + child: ElevatedButton( + child: const Text('Show MaterialBanner'), + onPressed: () => ScaffoldMessenger.of(context).showMaterialBanner( + MaterialBanner( + content: const Text('Hello, I am a Material Banner'), + leading: const Icon(Icons.info), + backgroundColor: Colors.yellow, + actions: [ + TextButton( + child: const Text('Dismiss'), + onPressed: () => ScaffoldMessenger.of(context) + .hideCurrentMaterialBanner(), + ), + ], + ), + ), + ), + ), + ); +} + +``` + +The [Material guidelines for banners](https://material.io/components/banners#usage) state that your app should show only one at a time, so if your app calls `showMaterialBanner` more than once, the `ScaffoldMessenger` will maintain a queue, showing each new banner as the previous one is dismissed. Thanks to [Calamity210](https://github.com/Calamity210) for this great addition to the Material support in Flutter! + +Building further on Flutter 2.0 and its new text editing features, like text selection pivot points and being able to stop propagation of a keyboard event once it’s been handled, in this release, we’ve added the ability to make text editing keyboard shortcuts overridable ([#85381](https://github.com/flutter/flutter/pull/85381)). If you want **Ctrl-A** to do something custom instead of selecting all text, you can do that. The [`DefaultTextEditingShortcuts`](https://github.com/flutter/flutter/blob/b524270af147847f64fa296cf6eed3633ffe683d/packages/flutter/lib/src/widgets/default_text_editing_shortcuts.dart#L163) class contains a list of every keyboard shortcut supported by Flutter on each platform. If you want to override anything, use Flutter’s existing [`Shortcuts`](https://api.flutter.dev/flutter/widgets/Shortcuts-class.html) widget to remap any shortcut to an existing or custom intent. You can place that widget anywhere in your widget tree where you want the override to apply. Check out some examples in the [API reference](https://api.flutter.dev/flutter/widgets/DefaultTextEditingShortcuts-class.html). + +## Plugins: camera, image picker & plus plugins + +Another plugin that’s seen a lot of improvements is [the camera plugin](https://pub.dev/packages/camera): + +* [3795](https://github.com/flutter/plugins/pull/3795) [camera] android-rework part 1: Base classes to support Android Camera features + +* [3796](https://github.com/flutter/plugins/pull/3796) [camera] android-rework part 2: Android auto focus feature + +* [3797](https://github.com/flutter/plugins/pull/3797) [camera] android-rework part 3: Android exposure related features + +* [3798](https://github.com/flutter/plugins/pull/3798) [camera] android-rework part 4: Android flash and zoom features + +* [3799](https://github.com/flutter/plugins/pull/3799) [camera] android-rework part 5: Android FPS range, resolution and sensor orientation features + +* [4039](https://github.com/flutter/plugins/pull/4039) [camera] android-rework part 6: Android exposure- and focus point features + +* [4052](https://github.com/flutter/plugins/pull/4052) [camera] android-rework part 7: Android noise reduction feature + +* [4054](https://github.com/flutter/plugins/pull/4054) [camera] android-rework part 8: Supporting modules for final implementation + +* [4010](https://github.com/flutter/plugins/pull/4010) [camera] Do not trigger flat device orientation on iOS + +* [4158](https://github.com/flutter/plugins/pull/4158) [camera] Fix coordinate rotation for setting focus- and exposure points on iOS + +* [4197](https://github.com/flutter/plugins/pull/4197) [camera] Fix camera preview not always rebuilding on orientation change + +* [3992](https://github.com/flutter/plugins/pull/3992) [camera] Prevent crash when setting unsupported FocusMode + +* [4151](https://github.com/flutter/plugins/pull/4151) [camera] Introduce camera_web package + +There’s also been a lot of work on [the image_picker plugin](https://pub.dev/packages/image_picker) focusing on the end-to-end camera experience: + +* [3898](https://github.com/flutter/plugins/pull/3898) [image_picker] Image picker fix camera device + +* [3956](https://github.com/flutter/plugins/pull/3956) [image_picker] Change storage location for camera captures to internal cache on Android, to comply with new Google Play storage requirements + +* [4001](https://github.com/flutter/plugins/pull/4001) [image_picker] Removed redundant request for camera permission + +* [4019](https://github.com/flutter/plugins/pull/4019) [image_picker] Fix rotation when camera is a source + +This work improves the functionality and robustness of the camera and image_picker plugins for Android. In addition, you’ll notice an early version of the [camera plugin](https://pub.dev/packages/camera_web) is available with web support ([#4151](https://github.com/flutter/plugins/pull/4151)). This preview provides basic support for viewing camera preview, taking a picture, using flash and zoom controls, all on the web. It is currently not an [endorsed plugin](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin), so you will need to [add it explicitly](https://pub.dev/packages/camera_web/install) to be used in your web app. + +The initial Android camera rewrite work was contributed by [acoutts](https://github.com/acoutts). The camera and image_picker work was landed by [Baseflow](https://www.baseflow.com/open-source/flutter), a consulting firm specializing in Flutter and well-known for [their own packages on pub.dev](https://pub.dev/publishers/baseflow.com/packages). The camera_web work was largely done by [Very Good Ventures](https://verygood.ventures/), a US-based Flutter consulting firm. Many thanks to all of you for your contributions to the Flutter community! + +Another valuable community contribution is by the Flutter Community organization, known for [the “plus” plugins](https://plus.fluttercommunity.dev/). With this release of Flutter, each of the corresponding plugins from the Flutter team now carry a recommendation just like this one for [battery](https://pub.dev/packages/battery): + +<DashImage figure src="images/0rvrCGEZ-vkRxga-n.webp" /> + + +Furthermore, since these plugins are no longer actively maintained, they are no longer marked as Flutter Favorite plugins. If you haven’t already done so, we recommend moving to the plus versions of the following plugins: + +<DashImage figure src="images/1YX-xNXUTKnNuZNBAmt7CAQ.webp" /> + + +## Flutter DevTools: performance, Widget inspector, & polish + +This release of Flutter comes with a number of improvements to Flutter DevTools. First and foremost is the added support in DevTools to take advantage of engine updates ([#26205](https://github.com/flutter/engine/pull/26205), [#26233](https://github.com/flutter/engine/pull/26233), [#26237](https://github.com/flutter/engine/pull/26237), [#26970](https://github.com/flutter/engine/pull/26970), [#27074](https://github.com/flutter/engine/pull/27074), [#26617](https://github.com/flutter/engine/pull/26617)). One set of these updates enables Flutter to do a better job of associating trace events with specific frames, which helps developers to determine why a frame might be going over budget. You can see this reflected in the DevTools Frames chart that has been rebuilt to be “live”; frames are populated in this chart as they are rendered in your app. Selecting a frame from this chart navigates to the timeline events for that frame: + +<DashImage figure src="images/08evmW5s37nGUIc98.webp" /> + + +The Flutter engine now also identifies shader compilation events in the timeline. Flutter DevTools uses these events to help you diagnose shader compilation jank in your app. + +<DashImage figure src="images/0esYsdKzKL64130ln.webp" alt="DevTools detecting lost frames due to shader compilation" caption="DevTools detecting lost frames due to shader compilation" /> + + +With this new feature, DevTools detects when you’ve lost frames to shader compilation so that you can fix the issue. To run your app as if it’s the first time (before your shader cache has been populated as it would be for any user), use `flutter run`with the `--purge-persistent-cache` flag. This clears the cache to ensure you are reproducing the environment that users see for the “first run” or “re-open” (iOS) experience. This feature is still under development, so please [file issues](https://b.corp.google.com/issues/new?component=775375&template=1369639) for problems you find, or for any improvements we can make to help debug shader compilation jank. + +In addition, when you’re tracking down CPU performance issues in your app, you may have been swamped in profiling data from Dart and Flutter libraries and/or the native code of the engine. If you’d like to turn any of these off to focus on your own code, you can do that with the new CPU Profiler feature ([#3236](https://github.com/flutter/devtools/pull/3236)) that enables you to hide profiler information from any of these sources. + +<DashImage figure src="images/0IzjrKssfjooCGzKm.webp" /> + + +For any of the categories you don’t filter out, they’ve now been color-coded ([#3310](https://github.com/flutter/devtools/pull/3310), [#3324](https://github.com/flutter/devtools/pull/3324)) so that you can easily see what parts of the CPU Frame Chart come from what parts of the system. + +<DashImage figure src="images/0ewigNSGco1ozScc0.webp" alt="Colored frame chart to identify app vs. native vs. Dart vs. Flutter code activities in your app" caption="Colored frame chart to identify app vs. native vs. Dart vs. Flutter code activities in your app" /> + + +Performance isn’t the only thing that you’ll want to debug. This release of DevTools comes with an update to the Widget Inspector that allows you to hover over a widget to evaluate the object, view properties, widget state, and so on. + +<DashImage figure src="images/0cLXjl-F3Vj0bFkKg.webp" /> + + +And, when you select a widget, it automatically populates in the new Widget Inspector Console, where you can explore the widget’s properties. + +<DashImage figure src="images/0l8XoqqLcz6W0CkRE.webp" /> + + +When paused at a breakpoint, you can also evaluate expressions from the console. + +In addition to the new features, the Widget Inspector has undergone a facelift. To make DevTools a more useful destination for understanding and debugging your Flutter apps, we partnered with [Codemate](https://codemate.com), a creative tech agency in Finland, to make some updates. + +<DashImage figure src="images/0iN9uTsyfsUOt8cTw.webp" alt="Flutter DevTools polished UX for greater ease of use" caption="Flutter DevTools polished UX for greater ease of use" /> + + +In this screenshot, you can see the following changes: + +* **Better communicating what the debug toggle buttons do** — those buttons have new icons, task-oriented labels, as well as rich tooltips that describe what they do and when to use them. Each tooltip further links out to detailed documentation of the feature. + +* **Easier to scan and locate widgets of interest** — frequently used widgets from the Flutter framework now show icons in the widget tree view on the left hand side of the inspector. They’re further color coded based on their categories. For example, layout widgets are displayed in blue while content widgets are displayed in green. Furthermore, each Text widget now shows a preview of its content. + +* **Aligning the color scheme of the layout explorer and the widget tree** — it’s now easier to identify the same widget from the layout explorer and the widget tree. For example, the “Column” widget in the screenshot below is on a blue background in the layout explorer, and it has a blue icon in the widget tree view. + +We’d love to [hear your thoughts](https://github.com/flutter/devtools/issues) about any issues that stem from these updates or any other improvements we can make to ensure that DevTools works great. And these highlights are just the start. For a full list of what’s new in DevTools with this release of Flutter, check out the release notes: + +* [Flutter DevTools 2.3.2 Release Notes](https://groups.google.com/g/flutter-announce/c/LSNbc2rKIjQ/m/7Y7cWgO2CQAJ) + +* [Flutter DevTools 2.4.0 Release Notes](https://groups.google.com/g/flutter-announce/c/qenYe5LuHb8/m/tkWcBCVaAQAJ) + +* [Flutter DevTools 2.6.0 Release Notes](https://groups.google.com/g/flutter-announce/c/yBEZOWdV9nc/m/KCX3m2BpCAAJ) + +## IntelliJ/Android Studio: integration tests, test coverage, and icon previews + +The IntelliJ/Android Studio plugin for Flutter has also undergone a number of improvements with this release, starting with the ability to run integration tests ([#5459](https://github.com/flutter/flutter-intellij/pull/5459)). Integration tests are whole-app tests that run on a device, live in the integration_test directory and use the same `testWidgets()` functionality from widget unit tests. + +<DashImage figure src="images/0kTKO_OcWee8OS0Lo.webp" alt="Integration testing your Flutter app in IntelliJ/Android Studio" caption="Integration testing your Flutter app in IntelliJ/Android Studio" /> + + +To add an integration test to your project, [follow the instructions on flutter.dev](https://flutter.dev/docs/testing/integration-tests). To connect the test with IntelliJ or Android Studio, add a run configuration that launches the integration tests and connect a device for the test to use. Running the configuration allows you to run the test, including setting breakpoints, stepping, etc. + +In addition, the latest IJ/AS plugin for Flutter allows you to see the coverage information for both unit test and integration test runs. You can access this from the toolbar button next to the “Debug” button: + +<DashImage figure src="images/0vxdeNoUw3boFaeFu.webp" /> + + +Coverage info is displayed using red and green bars in the gutter of the editor. In this example, lines 9–13 were tested, but lines 3 and 4 were not. + +<DashImage figure src="images/0G5M8qrjxw3gyeT7a.webp" /> + + +The latest release also includes the new ability to preview icons used from packages from pub.dev built around TrueType font files ([#5504](https://github.com/flutter/flutter-intellij/pull/5504), [#5595](https://github.com/flutter/flutter-intellij/pull/5595), [#5677](https://github.com/flutter/flutter-intellij/pull/5677), [#5704](https://github.com/flutter/flutter-intellij/pull/5704)), just as the Material and Cupertino icons support previewing. + +<DashImage figure src="images/0TK2px5OYQ_ggn2jx.webp" alt="Icon preview in IntelliJ/Android Studio" caption="Icon preview in IntelliJ/Android Studio" /> + + +To enable icon previews you need to tell the plugin which packages you are using. There is a new text field in the plugin settings/preferences page: + +<DashImage figure src="images/0GTrbuGlv9G-YPvOi.webp" /> + + +Note that this works for icons defined as static constants in a class, as shown in the sample code in the screen shot. It won’t work for expressions, such as`LineIcons.addressBook()` or `LineIcons.values['code']`. If you are the author of an icon package that does NOT work with this feature, please create an [issue](https://github.com/flutter/flutter-intellij/issues). + +That are lots more updates for the IntelliJ/Android Studio plugin for Flutter that you can read about in the release notes: + +* [Flutter IntelliJ Plugin M57 Release](https://groups.google.com/g/flutter-announce/c/nZPj0uIW3h4/m/2Xnx8KQtAwAJ) + +* [Flutter IntelliJ Plugin M58 Release](https://groups.google.com/g/flutter-announce/c/WJUH0m6cu-U/m/_n0RltLFAAAJ) + +* [Flutter IntelliJ Plugin M59 Release](https://groups.google.com/g/flutter-announce/c/CNzqxtybpBA/m/nSu7QabMAQAJ) + +* [Flutter IntelliJ Plugin M60 Release](https://groups.google.com/g/flutter-announce/c/qc40yulxvAg/m/B_1HuGmoBQAJ) + +## Visual Studio Code: dependencies, Fix All, and Test Runner + +The Visual Studio Code plugin for Flutter has also improved in this release, beginning with two new commands “Dart: Add Dependency” and “Dart: Add Dev Dependency” ([#3306](https://github.com/Dart-Code/Dart-Code/issues/3306), [#3474](https://github.com/Dart-Code/Dart-Code/issues/3474)). + +<DashImage figure src="images/0S8Nl6GbXHBHV64Rr.webp" alt="Adding a Dart dependency in Visual Studio Code" caption="Adding a Dart dependency in Visual Studio Code" /> + + +These commands provide functionality like [the Pubspec Assist plugin by Jeroen Meijer](https://marketplace.visualstudio.com/items?itemName=jeroen-meijer.pubspec-assist) has been providing for awhile now. These new commands come right out of the box and provide a type-to-filter list of packages periodically fetched from pub.dev. + +You may also be interested in the “Fix All” command ([#3445](https://github.com/Dart-Code/Dart-Code/issues/3445), [#3469](https://github.com/Dart-Code/Dart-Code/issues/3469)) that’s available for Dart files and can fix all of the same issues as [dart fix](https://dart.dev/tools/dart-fix) for the current open file in one step. + +<DashImage figure src="images/0dzvJ63ojTXw-Grsu.webp" alt="Using Flutter Fix rules to fix all known issues in your code" caption="Using Flutter Fix rules to fix all known issues in your code" /> + + +This can also be set to run on-save by adding `source.fixAll` to the `editor.codeActionsOnSave` VS Code setting. + +Or, if you’d like to give the preview feature a try, you can enable the `dart.previewVsCodeTestRunner` setting and see Dart and Flutter tests run via the new Visual Studio Code test runner. + +<DashImage figure src="images/0chKF4N2FzPBN3R3G.webp" alt="Using the new Visual Studio Code test runner test your Dart and Flutter code" caption="Using the new Visual Studio Code test runner test your Dart and Flutter code" /> + + +The Visual Studio Code test runner looks a little different than the current Dart and Flutter test runner and will persist results across sessions. The Visual Studio Code test runner also adds new gutter icons showing the last state of a test that can be clicked to run the test (or right-clicked for a context menu). + +<DashImage figure src="images/0snvlKGuZPa9zHFBc.webp" /> + + +In coming releases, the existing Dart and Flutter test runner will be removed in favor of the new Visual Studio Code test runner. + +And this is just the tip of the iceberg with new Visual Studio Code features and fixes. For all of the details, check out the release notes: + +* [v3.26](https://dartcode.org/releases/v3-26/) VS Code Test Runner integration, Flutter Create Settings, … + +* [v3.25](https://dartcode.org/releases/v3-25/) Additional Dependency Management improvements, Fix All in File / On-Save, … + +* [v3.24](https://dartcode.org/releases/v3-24/) Dependencies tree improvements, Easier launch configurations, Editor improvements + +* [v3.23](https://dartcode.org/releases/v3-23/) Profile Mode improvements, Improved dependencies tree, LSP improvements + +## Tools: exceptions, new app template & Pigeon 1.0 + +In previous versions of Flutter, you may have been frustrated by exceptions that you expected to be unhandled so that you could trigger the debugger and figure out where they originated only to find that the Flutter framework did not let the exception through to trigger the “unhandled expectation” handler in your debugger. In this release, debuggers now break correctly on unhandled exceptions that previously were just caught by the framework ([#17007](https://github.com/flutter/flutter/issues/17007)). This improves the debugging experience as your debugger can now point you directly to the throwing line in their code instead of pointing to a random line deep in the framework. A related new feature enables you to decide if a FutureBuilder should rethrow or swallow errors (#[84308](https://github.com/flutter/flutter/pull/84308)). This should give you a large number of additional exceptions to help track down the issues in your Flutter apps. + +Since the dawn of Flutter, there has been the Counter app template, which has many good qualities: it shows off many features of the Dart language, demonstrates several key Flutter concepts and it’s small enough to fit into a single file, even with a lot of explanatory comments. However, what it doesn’t do is provide a particularly good jumping off point for a real-world Flutter app. In this release, there’s a new template ([#83530](https://github.com/flutter/flutter/pull/83530)) available via the following command: + +`$ flutter create -t skeleton my_app` + +<DashImage figure src="images/03utkTiVxsZOcvcKl.webp" alt="*The new Flutter skeleton template in action*" caption="*The new Flutter skeleton template in action*" /> + + +The skeleton template generates a two-page List View Flutter app (with Detail View) that follows community best practices. It was developed with a great deal of internal and external review to provide a better base on which to build a production quality app and supports the following features: + +* Uses `ChangeNotifier` to coordinate multiple widgets + +* Generates localizations by default using arb files + +* Includes an example image and establishes 1x, 2x, and 3x folders for image assets + +* Uses a “feature-first” folder organization + +* Supports shared preferences + +* Supports light and dark theming + +* Supports navigation between multiple pages + +Over time, as Flutter best practices evolve, expect this new template to evolve with them. + +If, on the other end of the spectrum, you’re developing a plugin and not an app, you may be interested in the 1.0 release of Pigeon. Pigeon is a codegen tool for generating typesafe interop code between Flutter and its host platform. It allows you to define a description of your plugin’s API and generate skeleton code for Dart, Java, and Objective-C (which are accessible to Kotlin and Swift, respectively). + +<DashImage figure src="images/0CfaqXymLvWZc6Cqo.webp" alt="Sample generated Pigeon code" caption="Sample generated Pigeon code" /> + + +Pigeon is already used in some of the plugins from the Flutter team. With this release, which provides more helpful error messages, added support for generics, primitive data types as arguments and return types, and multiple arguments, expect it to be used more heavily in the future. If you’d like to take advantage of Pigeon in your own plugin or add-to-app projects, you can find out more information at [the pigeon plugin page](https://pub.dev/packages/pigeon). + +## Breaking changes & deprecations + +The following are the breaking changes in the Flutter 2.5 release: + +* [Default drag scrolling devices](https://flutter.dev/docs/release/breaking-changes/default-scroll-behavior-drag) + +* [Deprecated API removed after v2.2](https://flutter.dev/docs/release/breaking-changes/2-2-deprecations) + +* [Introducing package:flutter_lints](https://flutter.dev/docs/release/breaking-changes/flutter-lints-package) + +* [ThemeData’s accent properties have been deprecated](https://flutter.dev/docs/release/breaking-changes/theme-data-accent-properties) + +* [GestureRecognizer Cleanup](https://flutter.dev/docs/release/breaking-changes/gesture-recognizer-add-allowed-pointer) + +* [Replace AnimationSheetBuilder.display with collate](https://flutter.dev/docs/release/breaking-changes/animation-sheet-builder-display) + +* [Using HTML slots to render platform views in the web](https://flutter.dev/docs/release/breaking-changes/platform-views-using-html-slots-web) + +* [Migrate LogicalKeySet to SingleActivator](https://github.com/flutter/flutter/pull/80756) + +For the full list of breaking changes since the 1.17 release, [see flutter.dev](https://flutter.dev/docs/release/breaking-changes). + +As we continue to update Flutter Fix (available in your IDE and via the `dart fix` command), we have a grand total of 157 rules to automatically migrate your code affected by these or past breaking changes, as well as any deprecations. As always, many thanks to the community for [contributing tests](https://github.com/flutter/tests/blob/master/README.md), they help us identify these breaking changes. To learn more, check out [our breaking change policy](https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes). + +Also, with the Flutter 2.5 release, we are deprecating support for iOS 8 [as announced in Sept, 2020](http://flutter.dev/go/rfc-ios8-deprecation). Dropping support for iOS 8, which has less than 1% market share, allows the Flutter team to focus on new platforms in wider usage. Deprecation means that these platforms may work but that we will not test new versions of Flutter or the plugins on these platforms. You can see the list of [currently supported Flutter platforms on flutter.dev](https://flutter.dev/docs/development/tools/sdk/release-notes/supported-platforms). + +## Summary + +In closing, thank you as always to the Flutter community around the world that makes all of this possible. To the hundreds of developers who have contributed and reviewed the 1000s of PRs in this update, here’s to the fruits of each of your efforts. Together, we’re working to transform the app development process for developers around the world so you can ship more, faster, deploying to the platforms you care about from a single codebase. + +Stay tuned for more updates from all of us on the Flutter team at Google. The year is not over yet! \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/00BHU6qlHwKUxPrXb.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/00BHU6qlHwKUxPrXb.webp new file mode 100644 index 0000000000..0d26ea43c5 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/00BHU6qlHwKUxPrXb.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/00frogkEKgpNOgCVF.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/00frogkEKgpNOgCVF.webp new file mode 100644 index 0000000000..0c55d94769 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/00frogkEKgpNOgCVF.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/04T03yBAog-6unwc8.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/04T03yBAog-6unwc8.webp new file mode 100644 index 0000000000..0dcf80f927 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/04T03yBAog-6unwc8.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/05K_qZZ3Sb4n8tXiW.gif b/sites/www/content/blog/whats-new-in-flutter-2-8/images/05K_qZZ3Sb4n8tXiW.gif new file mode 100644 index 0000000000..9f2a478fd2 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/05K_qZZ3Sb4n8tXiW.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/069rsgRC_hcraKOAK.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/069rsgRC_hcraKOAK.webp new file mode 100644 index 0000000000..2d39c843b7 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/069rsgRC_hcraKOAK.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/09zL2daapFy48CypY.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/09zL2daapFy48CypY.webp new file mode 100644 index 0000000000..482d93428f Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/09zL2daapFy48CypY.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/0QXiOV7Bm7wlP9gI-.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0QXiOV7Bm7wlP9gI-.webp new file mode 100644 index 0000000000..421ac8a1d7 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0QXiOV7Bm7wlP9gI-.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/0SBoQZBrfuLVhEkdW.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0SBoQZBrfuLVhEkdW.webp new file mode 100644 index 0000000000..cd5356804f Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0SBoQZBrfuLVhEkdW.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/0_l1bJAoLwWB5DBdq.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0_l1bJAoLwWB5DBdq.webp new file mode 100644 index 0000000000..f90cc94fbb Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0_l1bJAoLwWB5DBdq.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/0ax1aVB0JnPAllO4a.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0ax1aVB0JnPAllO4a.webp new file mode 100644 index 0000000000..f30bf7e30a Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0ax1aVB0JnPAllO4a.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/0gE84QAGGECM-tFAH.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0gE84QAGGECM-tFAH.webp new file mode 100644 index 0000000000..d165bb5510 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0gE84QAGGECM-tFAH.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/0hRLh9gi5n3E82lXG.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0hRLh9gi5n3E82lXG.webp new file mode 100644 index 0000000000..7733dc28f9 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0hRLh9gi5n3E82lXG.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/0jpo2j3C5Yo7HwuKW.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0jpo2j3C5Yo7HwuKW.webp new file mode 100644 index 0000000000..4043ed8644 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0jpo2j3C5Yo7HwuKW.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/0ltUPM1ji3asrgDOw.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0ltUPM1ji3asrgDOw.webp new file mode 100644 index 0000000000..4adeedc0f6 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0ltUPM1ji3asrgDOw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/0raIVwhHpVh0ON5ha.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0raIVwhHpVh0ON5ha.webp new file mode 100644 index 0000000000..9697fc13fa Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0raIVwhHpVh0ON5ha.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/0u_epPOxdzkf_z2W1.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0u_epPOxdzkf_z2W1.webp new file mode 100644 index 0000000000..e25723b260 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0u_epPOxdzkf_z2W1.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/0xpb0al6JT1AdKUjO.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0xpb0al6JT1AdKUjO.webp new file mode 100644 index 0000000000..a9f064ef50 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0xpb0al6JT1AdKUjO.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/0xt35ZmY8r4BlFvfx.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0xt35ZmY8r4BlFvfx.webp new file mode 100644 index 0000000000..aedf604fbc Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0xt35ZmY8r4BlFvfx.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/0y2N78Oatw9te8kPh.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0y2N78Oatw9te8kPh.webp new file mode 100644 index 0000000000..5d11ddf2aa Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/0y2N78Oatw9te8kPh.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/images/1eAFKwrcfLHlqKk6ng677pw.webp b/sites/www/content/blog/whats-new-in-flutter-2-8/images/1eAFKwrcfLHlqKk6ng677pw.webp new file mode 100644 index 0000000000..30184813ee Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-2-8/images/1eAFKwrcfLHlqKk6ng677pw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-2-8/index.md b/sites/www/content/blog/whats-new-in-flutter-2-8/index.md new file mode 100644 index 0000000000..e2dc773108 --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-2-8/index.md @@ -0,0 +1,524 @@ +--- +title: "What’s New in Flutter 2.8" +description: "Performance improvements, new Firebase features, desktop status, tooling updates and more!" +publishDate: 2021-12-09 +author: csells +image: images/00frogkEKgpNOgCVF.webp +category: releases +layout: blog +--- + +It’s that time of year in the northern hemisphere: the leaves are turning, the temperature is cooling and the final stable release of the year is here. Hello and welcome to Flutter 2.8! This release represents the hard work of 207 contributors and 178 reviewers, producing 2,424 PRs merged and 2976 issues closed. Special thanks to the top community contributor of this release, Bartosz Selwesiuk, a Flutter Engineer at Very Good Ventures, who provided 23 PRs, most of which were primarily “focused” (sic) around the camera plugin for web. + +All of this collective work produced significant performance improvements in both the engine and in Flutter DevTools, a stable release of the Google Mobile Ads SDK for Flutter, a slew of new Firebase features and improvements, WebView 3.0, a new batch of Flutter Favorite packages, a raft of updates to desktop on our way to a stable release, and a new version of DartPad with support for more packages, including Firebase itself. This might be the last release of the year but it’s by no means the least. Let’s get to it! + +## Performance + +As always, job #1 with Flutter is quality. We spend much of our time ensuring that Flutter runs as smoothly and as robustly as it can across the range of supported devices. + +## Startup + +This release includes improvements to application startup latency. Testing these improvements against Google Pay, a large, popular app with more than 1 million lines of code to ensure that these changes result in perceivable impact in the real-world. All together these improvements have resulted in reduction in startup latency for Google Pay of 50% when running on a low-end Android device, and a 10% improvement on high-end devices. + +Improvements to the way that Flutter influences Dart VM garbage collection policy now helps avoid ill-timed GC cycles during the application startup sequence. For example, before the first frame is rendered on Android, Flutter now [only notifies the Dart VM of memory pressure for `TRIM_LEVEL_RUNNING_CRITICAL` and above](https://github.com/flutter/flutter/issues/90551) signals. In local testing, this change reduced the time to first frame by up to 300ms on a low-end device. + +Due to an [abundance of caution](https://github.com/flutter/engine/pull/29145#pullrequestreview-778935616), in previous releases, Flutter blocked the platform thread while creating platform views. [Careful reasoning and testing](https://github.com/flutter/flutter/issues/91711) determined that some of the serialization could be removed, which removed >100ms of blockage during startup of Google Pay on a low-end device. + +Previously, setting up the default font manager introduced an artificial delay when setting up the first Dart isolate. [Delaying the default font manager setup](https://github.com/flutter/engine/pull/29291) to run concurrently with Dart `Isolate` setup both improved startup latency, and made the effects of the above optimizations much more visible, as this was the primary bottleneck. + +## Memory + +Flutter developers targeting memory constrained devices [were having problems](https://github.com/flutter/flutter/issues/91382) taking performance traces due to Flutter eagerly loading the Dart VM’s “service isolate”, whose AOT code was bundled with the app such that Flutter was reading both into memory simultaneously. For Android in the 2.8 release, the Dart VM’s service isolate [was split into its own bundle](https://github.com/flutter/engine/pull/29245) that can be loaded separately, which results in a memory savings of up to 40 MB until the service isolate is needed. The memory footprint has been further reduced by up to 10% by the [Dart VM informing the OS](https://github.com/flutter/flutter/issues/92120) that the pages in memory used by the AOT program are backed by a file that likely won’t need to be read again. Therefore, the pages that had been holding a copy of the file-backed data can then be reclaimed and put to other uses. + +## Profiling + +Sometimes developers want to view performance trace data from Flutter alongside Android native tracing events. Furthermore, they’d often like to see trace events even in release mode builds to get a better understanding of performance issues in their deployed applications. To that end, [the 2.8 stable release](https://github.com/flutter/engine/pull/28903) now sends tracing events to the Android systrace recorder if it is enabled at application startup, and these events are sent even when the Flutter application is built in release mode. + +<DashImage figure src="images/1eAFKwrcfLHlqKk6ng677pw.webp" alt="Flutter tracing events are now shown (bottom) in the Android systrace recording tooling" caption="Flutter tracing events are now shown (bottom) in the Android systrace recording tooling" /> + + +Additionally, to help create animations with less jank, some of you wanted [more information](https://github.com/flutter/flutter/issues/92286) in performance traces about the behavior of the raster cache, which allows Flutter to blit expensive, reused pictures instead of re-drawing them on each frame. [New flow events](https://github.com/flutter/flutter/issues/92286) in performance traces now allow you to track the lifetimes of raster cached pictures. + +## Flutter DevTools + +For debugging performance issues, this release of DevTools adds a [new “Enhance Tracing” feature](https://github.com/flutter/devtools/pull/3451) to help you diagnose UI jank stemming from expensive build, layout, and paint operations. + +<DashImage figure src="images/0QXiOV7Bm7wlP9gI-.webp" /> + + +When any of these tracing features are enabled, the Timeline includes new events for widgets built, render objects laid out, and render objects painted, as appropriate. + +<DashImage figure src="images/0ltUPM1ji3asrgDOw.webp" /> + + +In addition, this release of DevTools adds new [support for profiling the performance of your app’s startup](https://github.com/flutter/devtools/pull/3357). This profile contains CPU samples from the initialization of the Dart VM up until the first Flutter frame has been rendered. After you press the “Profile app start up” button and the app startup profile has been loaded, you will see that the “AppStartUp” user tag is selected for the profile. You can also load the app startup profile by selecting this user tag filter, when present, in the list of available user tags. Selecting this tag shows your profile data for your app’s startup. + +<DashImage figure src="images/0xpb0al6JT1AdKUjO.webp" /> + + +## Web platform views + +Android and iOS aren’t the only platforms getting performance improvements. This release also improves the performance of platform views for Flutter web. If you’re unfamiliar with platform views, they’re the way that Flutter allows you to host native UI components from the underlying platform in your app. Flutter web implements this with the [`HtmlElementView`](https://api.flutter.dev/flutter/widgets/HtmlElementView-class.html) widget, which allows you to host HTML elements inside your Flutter web app. If you’re using the web versions of the google_maps_flutter plugin or the video_player plugin, or you’re following the Flutter team’s advice about how to optimize the [display images on the web](https://docs.flutter.dev/development/platform-integration/web-images#use-img-in-a-platform-view), then you’re using platform views. + +In previous versions of Flutter, embedding a platform view immediately created a new canvas and each extra platform view added another canvas. The extra canvases are expensive to create, as each is the size of the entire window. This release [reuses canvases created for earlier platform views](https://github.com/flutter/engine/pull/28087) so, rather than incurring the cost 60x per second, you incur the cost once in the entire lifetime of the app. This means that you can have multiple `HtmlElementView` instances in your web apps without degrading performance, while also reducing scrolling jank when platform views are used. + +## Ecosystem + +Flutter isn’t just the framework, the engine, and the tools — there are more than 20,000 Flutter-compatible packages and plugins on pub.dev with more added every day. A significant amount of what Flutter developers interact with day-to-day is part of the larger ecosystem, so let’s take a look at what’s been going on in the Flutter ecosystem since the previous release. + +## GA of Flutter Ads + +First and foremost is the [release of the Google Mobile SDK for Flutter into general availability](https://medium.com/flutter/announcing-general-availability-for-the-google-mobile-ads-sdk-for-flutter-574e51ea6783) in November. + +<DashImage figure src="images/0hRLh9gi5n3E82lXG.webp" /> + + +This release supports 5 ad formats, integrates both AdMob and Ad Manager support and includes a beta of a new mediation feature to help you optimize ad performance. For more information about integrating Google Ads into your Flutter app as well as other monetization options, [check out the new monetization page on flutter.dev](https://flutter.dev/monetization). + +## WebView 3.0 + +Another new release that comes with Flutter this time around is the 3.0 release of [the webview_flutter plugin](https://pub.dev/packages/webview_flutter). We’ve bumped the version number because of the number of new features but also because of a potentially breaking change in the way web views work on Android. In previous versions of `webview_flutter`, the hybrid composition mode has been available but not the default. Hybrid composition fixes a number of issues that the previous default virtual displays mode has. Based on user feedback and issue tracking, we think it’s time for hybrid composition to become the default. In addition, `webview_flutter` also adds a number of highly requested features: + +* Support for POST as well as GET for populating content ([4450](https://github.com/flutter/plugins/pull/4450), [4479](https://github.com/flutter/plugins/pull/4479), [4480](https://github.com/flutter/plugins/pull/4480), [4573](https://github.com/flutter/plugins/pull/4573)) + +* Loading HTML from files and strings ([4446](https://github.com/flutter/plugins/pull/4446), [4486](https://github.com/flutter/plugins/pull/4486), [4544](https://github.com/flutter/plugins/pull/4544), [4558](https://github.com/flutter/plugins/pull/4558)) + +* Transparent background support ([3431](https://github.com/flutter/plugins/pull/3431), [4569](https://github.com/flutter/plugins/pull/4569), [4570](https://github.com/flutter/plugins/pull/4570)) + +* Writing cookies prior to loading content ([4555](https://github.com/flutter/plugins/pull/4555), [4556](https://github.com/flutter/plugins/pull/4556), [4557](https://github.com/flutter/plugins/pull/4557)) + +Furthermore, in the 3.0 release, `webview_flutter` provides preliminary support for a new platform: the web. We’ve had many of you ask for the ability to host a webview in a Flutter web app, which allows you to build an app for mobile *or* web from a single code base. What does hosting a webview look like in a Flutter web app? Well, from a coding perspective, it looks just the same: + +```dart +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:webview_flutter/webview_flutter.dart'; +import 'package:webview_flutter_web/webview_flutter_web.dart'; + +void main() { + runApp(const MaterialApp(home: HomePage())); +} + +class HomePage extends StatefulWidget { + const HomePage({Key? key}) : super(key: key); + + @override + State<HomePage> createState() => _HomePageState(); +} + +class _HomePageState extends State<HomePage> { + @override + void initState() { + super.initState(); + + // required while web support is in preview + if (kIsWeb) WebView.platform = WebWebViewPlatform(); + } + + @override + Widget build(BuildContext context) => Scaffold( + appBar: AppBar(title: const Text('Flutter WebView example')), + body: const WebView(initialUrl: 'https://flutter.dev'), + ); +} +``` + +When running on the web, it works as you’d expect: + +<DashImage figure src="images/04T03yBAog-6unwc8.webp" /> + + +Note that the current implementation of `webview_flutter` for web has a number of limitations based around the fact that it’s built using an `iframe`, which only supports simple URL loading and has no ability to control or interact with the loaded content (for more info, check out [the webview_flutter_web README](https://pub.dev/packages/webview_flutter_web)). However, we’re making `webview_flutter_web` available due to popular demand as [an unendorsed plugin](https://docs.flutter.dev/development/packages-and-plugins/developing-packages#endorsed-federated-plugin). If you want to give it a try, add the following line to your pubspec.yaml: + +```yaml +dependencies: + webview_flutter: ^3.0.0 + webview_flutter_web: ^0.1.0 # add unendorsed plugin explicitly +``` + +If you have feedback on webview_flutter v3.0, either on the web or off, please [log them on the Flutter repo as a webview issue](https://github.com/flutter/flutter/issues). Also, if you haven’t used webview before or you’d like a refresher, check out [the new webview codelab](https://codelabs.developers.google.com/codelabs/flutter-webview), which takes you step-by-step through the process of hosting web content in your Flutter app. + +## Flutter Favorites + +The [Flutter Ecosystem Committee](https://docs.flutter.dev/development/packages-and-plugins/favorites#flutter-ecosystem--committee) has met again to designate the following as Flutter Favorite packages: + +* Three custom router packages for the new Router API: [`beamer`](https://pub.dev/packages/beamer), [`routemaster`](https://pub.dev/packages/routemaster), and [`go_router`](https://pub.dev/packages/go_router) + +* [`drift`](https://pub.dev/packages/drift), a rename of an already capable and popular reactive persistence library for Flutter and Dart, built on top of [`sqlite`](https://pub.dev/packages/sqlite3) + +* [`freezed`](https://pub.dev/packages/freezed), a Dart “language patch” to provide a simple syntax for defining models, cloning objects, pattern matching, and more + +* [`dart_code_metrics`](https://pub.dev/packages/dart_code_metrics) + +* And several great looking GUI packages: [`flex_color_scheme`](https://pub.dev/packages/flex_color_scheme), [`flutter_svg`](https://pub.dev/packages/flutter_svg), [`feedback`](https://pub.dev/packages/feedback), [`toggle_switch`](https://pub.dev/packages/toggle_switch), and [`auto_size_text`](https://pub.dev/packages/auto_size_text) + +<DashImage figure src="images/0raIVwhHpVh0ON5ha.webp" alt="The Flexfold app is built with flex_color_scheme" caption="The Flexfold app is built with flex_color_scheme" /> + + +Congratulations to those package authors and thank you for supporting the Flutter community with your hard work. If you’re interested in nominating your favorite Flutter package for a Flutter Favorite award, follow the guidelines and instructions on [the Flutter Favorite program page](https://docs.flutter.dev/development/packages-and-plugins/favorites). + +## Platform-specific Packages + +If you are a package author, one decision you must make is which platforms you’re going to support. If you’re building a plugin with platform-specific native code, you can do that [using the `pluginClass` property in your project’s `pubspec.yaml`](https://docs.flutter.dev/development/packages-and-plugins/developing-packages#plugin-platforms), which indicates the native class that’s providing the functionality: + +```yaml +flutter: + plugin: + platforms: + android: + package: com.example.hello + pluginClass: HelloPlugin + ios: + pluginClass: HelloPlugin +``` + +However, as [Dart FFI](https://dart.dev/guides/libraries/c-interop) becomes more mature, it’s possible to implement platform-specific functionality in 100% Dart, like the [`path_provider_windows` package](https://pub.dev/packages/path_provider_windows) does. When you don’t have any native class to use but you still want to designate your package as supporting only certain platforms, use the `dartPluginClass` property instead: + +```yaml +flutter: + plugin: + implements: hello + platforms: + windows: + dartPluginClass: HelloPluginWindows +``` + +This setting in place, you have designates your package as only supporting certain platforms even if you don’t have any native code. You also must provide the Dart plugin class; learn more in [the Dart-only platform implementations docs on flutter.dev](https://docs.flutter.dev/development/packages-and-plugins/developing-packages#dart-only-platform-implementations). + +## Firebase + +Another big part of the Flutter ecosystem is FlutterFire, which is used by two-thirds of Flutter apps. This release adds a set of new features that make it easier to build applications using Flutter and Firebase: + +* All FlutterFire plugins are graduating from beta to stable + +* New support in DartPad for several Firebase services + +* New libraries to more easily build UI for authentication and live Firestore queries + +* New Firestore Object/Document Mapping for Flutter, available in Alpha + +## Production Quality + +[The FlutterFire plugins](http://firebase.flutter.dev) have (almost) all moved from beta to stable quality. + +<DashImage figure src="images/00BHU6qlHwKUxPrXb.webp" /> + + +Plugins moving to stable for Android, iOS, and web include [Analytics](https://firebase.flutter.dev/docs/analytics/overview), [Dynamic Links](https://firebase.flutter.dev/docs/dynamic-links/overview), [In-App Messaging](https://firebase.flutter.dev/docs/in-app-messaging/overview/), [Performance Monitoring](https://firebase.flutter.dev/docs/performance/overview), [Realtime Database](https://firebase.flutter.dev/docs/database/overview), [Remote Config](https://firebase.flutter.dev/docs/remote-config/overview) and, the new kid on the block, [Installations](https://firebase.flutter.dev/docs/installations/overview). The App Check plugin and macOS platform support are still in beta because of the phase of the Firebase libraries themselves, which are also in beta. If you’ve worried about whether to choose Realtime Database, Analytics, or Remote Config because the FlutterFire libraries weren’t ready for prime-time, worry no more. These are now fully supported plugins ready for production use. + +## Dart-only Firebase Initialization + +As packages have moved to production quality, we’ve added the ability to [initialize Firebase on any supported platform from Dart](https://github.com/FirebaseExtended/flutterfire/pull/6549): + +```dart +import 'package:firebase_core/firebase_core.dart'; +import 'firebase_options.dart'; // generated via `flutterfire` CLI + +Future<void> main() async { + // initialize firebase across all supported platforms + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); + + runApp(MyApp()); +} +``` + +This code initializes a Firebase app using the options appropriate for each supported platform as defined in the `firebase_options.dart` file, which contains per-platform data structures that look like this: + +```dart +static const FirebaseOptions web = FirebaseOptions( + apiKey: 'AIzaSyCZFKryCEiKhD0JMPeq_weJguspf09h7Cg', + appId: '1:111079797892:web:b9195888086158195ffed1', + messagingSenderId: '111079797892', + projectId: 'flutterfire-fun', + authDomain: 'flutterfire-fun.firebaseapp.com', + storageBucket: 'flutterfire-fun.appspot.com', + measurementId: 'G-K029Y6KJDX', +); +``` + +To gather the data for each platform’s initialization option data structure, check out [the new `flutterfire` CLI tool](https://pub.dev/packages/flutterfire_cli). + +<DashImage figure src="images/069rsgRC_hcraKOAK.webp" /> + + +This tool digs into the data in your platform-specific sub-folders to find the unique bundle ID and then uses that to look up the Firebase-project specific details for your matching platform-specific apps, even creating a new Firebase project and/or new platform-specific apps if there aren’t any. What this means for you is no more downloading and adding a `json` file to your Android project, downloading and adding a `plist` file to your iOS and macOS projects, or [pasting code into your web project’s index.html](https://github.com/FirebaseExtended/flutterfire/pull/7359) — no matter which supported Firebase platform you’re targeting, this single snippet of Dart code initializes Firebase for your app. Note that this may not be the only initialization you have to do to get your FlutterFire app working; for example, you might want to integrate the creation of your Crashlytics symbols into your [Android build](https://firebase.google.com/docs/crashlytics/get-started?platform=android#add-plugin) or your [iOS build](https://firebase.google.com/docs/crashlytics/get-started?platform=ios#set-up-dsym-uploading), but it should get it up and running in minutes with any new Firebase project. + +## Use Firebase with DartPad + +With this Dart-only initialization of FlutterFire, you can now use Firebase from within DartPad. + +<DashImage figure src="images/0y2N78Oatw9te8kPh.webp" /> + + +T[his example](https://dartpad.dev/?id=d57c6c898dabb8c6fb41018588b8cf73) demonstrates [a little chat app](https://twitter.com/puf/status/1458516522133909506) using Flutter, Firebase, and DartPad, all of which you can use right now with no installation. Today DartPad’s Firebase support already includes the core APIs, Authentication, and Firestore. Expect more Firebase services to appear in DartPad over time. + +The other thing that FlutterFire support in DartPad enables is the ability to use [an embedded instance of DartPad right in the docs](https://firebase.flutter.dev/docs/firestore/example/). + +<DashImage figure src="images/0_l1bJAoLwWB5DBdq.webp" /> + + +In this example, you’re seeing the docs for Cloud Firestore with the code for the [example application](https://github.com/FirebaseExtended/flutterfire/tree/master/packages/cloud_firestore/cloud_firestore/example) that you can run and edit directly in your browser without having to install a thing, create a test project, or even copy/paste the code. It’s all right there for your immediate use. + +## Firebase UI + +When integrating Firebase into an app, most apps have some kind of authentication flow. This includes allowing users to log in with email and password, or with a social auth provider like the one that Google provides. Firebase Authentication also allows for creating new accounts on the fly, validating email addresses, resetting passwords, and might even involve 2-step verification with SMS, logging in with a phone number, or even merging multiple user accounts into a single account. Firebase Authentication supports all of this functionality, but as a Flutter developer, you’re responsible for implementing the UI for all of it. Until today. + +Today, we’re pleased to announce that the [flutterfire_ui](https://pub.dev/packages/flutterfire_ui) package can create a basic authentication experience with a small amount of code. As an example, assume that you’ve got Email and Google authentication setup in your Firebase project: + +<DashImage figure src="images/09zL2daapFy48CypY.webp" /> + + +With this configuration, you can trigger an authentication experience as follows: + +```dart +import 'package:flutter/material.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutterfire_ui/auth.dart'; +import 'firebase_options.dart'; + +Future<void> main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) => MaterialApp( + home: AuthenticationGate(), + ); +} + +class AuthenticationGate extends StatelessWidget { + const AuthenticationGate({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) => StreamBuilder<User?>( + stream: FirebaseAuth.instance.authStateChanges(), + builder: (context, snapshot) { + // User is not signed in - show a sign-in screen + if (!snapshot.hasData) { + return SignInScreen( + providerConfigs: [ + EmailProviderConfiguration(), + GoogleProviderConfiguration( + clientId: 'xxxx-xxxx.apps.googleusercontent.com', + ), + ], + ); + } + + return HomePage(); // show your app’s home page after login + }, + ); +} +``` + +This initializes Firebase and, noticing that the user is not already logged in, shows the sign-in screen. The `SigninScreen` widget is configured with the Email and Google authentication providers. The code also listen to the user’s authentication state using the `firebase_auth` package, so once the user signs in you can display the rest of the application. With this code, you get a working login for all of the Firebase-supported platforms: Android, iOS, web, and macOS. + +With a little more configuration, you can easily add an image and some custom text (details are available in [the docs](https://firebase.flutter.dev/docs/ui/overview)), which gives you a full-featured login experience: + +<DashImage figure src="images/0u_epPOxdzkf_z2W1.webp" /> + + +This screenshot shows the mobile version, but because the `flutterfire_ui` screens are responsive, here’s what you get on a desktop device: + +<DashImage figure src="images/00frogkEKgpNOgCVF.webp" /> + + +If the user has an email/password already, they can log in and be done. If they use Google auth, they’ll be shown the normal Google auth flow, whether they’re on mobile, web, or desktop. If they don’t have an account already, they can press the button on the login screen and get to the registration screen. Once they’ve logged in or registered, there are flows for validating their email address, resetting their password, logging out, and linking social authentication accounts. Logging in by email works on all platforms along with social auth support for Google, Facebook, and Twitter, with partial support for Apple (it doesn’t work on Android). Authentication support in `flutterfire_ui` supports a number of scenarios and navigation schemes, along with customization and localization options. C[heck out the detailed docs and samples in the firebase.flutter.dev docs](https://firebase.flutter.dev/docs/ui/overview). + +Furthermore, authentication is not the only Firebase UI-related feature that flutterfire_ui supports. For showing users a live, infinite scroll list of data from a Firebase query, this release includes the `FirestoreListView`, which you can drop into your app with a live query as follows: + +```dart +class UserListView extends StatelessWidget { + UserListView({Key? key}) : super(key: key); + + // live Firestore query + final usersCollection = FirebaseFirestore.instance.collection('users'); + + @override + Widget build(BuildContext context) => Scaffold( + appBar: AppBar(title: const Text('Contacts')), + body: FirestoreListView<Map>( + query: usersCollection, + pageSize: 15, + primary: true, + padding: const EdgeInsets.all(8), + itemBuilder: (context, snapshot) { + final user = snapshot.data(); + + return Column( + children: [ + Row( + children: [ + CircleAvatar( + child: Text((user['firstName'] ?? 'Unknown')[0]), + ), + const SizedBox(width: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + '${user['firstName'] ?? 'unknown'} ' + '${user['lastName'] ?? 'unknown'}', + style: Theme.of(context).textTheme.subtitle1, + ), + Text( + user['number'] ?? 'unknown', + style: Theme.of(context).textTheme.caption, + ), + ], + ), + ], + ), + const Divider(), + ], + ); + }, + ), + ); +} +``` + +And this is what it looks like in action: + +<DashImage figure src="images/0jpo2j3C5Yo7HwuKW.webp" /> + + +Or, if you’d like to provide your users with the ability to create, read, update, and delete entries in a table, you’ve got the beginnings of that in `FirestoreDataTable`: + +```dart +class FirestoreTableStory extends StatelessWidget { + FirestoreTableStory({Key? key}) : super(key: key); + + // live Firestore query + final usersCollection = FirebaseFirestore.instance.collection('users'); + + @override + Widget build(BuildContext context) { + return FirestoreDataTable( + query: usersCollection, + columnLabels: const { + 'firstName': Text('First name'), + 'lastName': Text('Last name'), + 'prefix': Text('Prefix'), + 'userName': Text('User name'), + 'email': Text('Email'), + 'number': Text('Phone number'), + 'streetName': Text('Street name'), + 'city': Text('City'), + 'zipCode': Text('Zip code'), + 'country': Text('Country'), + }, + ); + } +} +``` + +Which works like this: + +<DashImage figure src="images/05K_qZZ3Sb4n8tXiW.gif" /> + + +For details about authentication, list views, and data tables, [check out the `flutterfire_ui` docs](https://firebase.flutter.dev/docs/ui/overview/). As this is a preview release, additional features are planned. If you have a question or a feature request, please [head over to the repo on GitHub to log issues or ask questions in the discussion section](https://github.com/FirebaseExtended/flutterfire/discussions/6978). + +## Firestore Object/Document Mapping + +And last but not least, one new feature to the integration between Firebase and Flutter that you should be aware of: the alpha release of [the Firestore Object/Document Mapper](https://firebase.flutter.dev/docs/firestore-odm/overview/). The Firestore ODM is aimed at helping Flutter developers be more productive by simplifying use of Firestore through familiar type-safe, structured objects and methods. Using code generation, the Firestore ODM improves the syntax for interacting with documents and collections by allowing you to model your data in a type-safe way: + +```dart +@JsonSerializable() +class Person { + Person({required this.name, required this.age}); + + final String name; + final int age; +} + +@Collection<Person>(‘/persons’) +final personsRef = PersonCollectionReference(); +``` + +With these types in place, you can now execute type-safe queries: + +```dart +personsRef.whereName(isEqualTo: 'Bob'); +personsRef.whereAge(isGreaterThan: 42); +``` + +The ODM also supports defining strongly typed sub-collections and provides built-in options for optimizing widget rebuilds with its `select` feature. You can read about all of this and more in [the Firestore ODM docs](https://firebase.flutter.dev/docs/firestore-odm/overview/). Since this is an alpha release, please provide feedback. To give feedback and ask questions, [join the Firestore ODM thread](https://github.com/FirebaseExtended/flutterfire/discussions/7475) of the FlutterFire discussion. + +## Desktop + +The Flutter 2.8 release includes another big step along the road to the stable release of Windows, macOS, and Linux. The quality bar is high, including internationalization and localization support like the recently landed [Chinese IME support](https://github.com/flutter/engine/pull/29620), [Korean IME support](https://github.com/flutter/engine/pull/24713), and [Kanji IME support](https://github.com/flutter/engine/pull/29761). Or, like the tight integration being built into [Windows accessibility support](https://github.com/flutter/flutter/issues/77838). It’s not enough for Flutter to run on desktop on the stable channel (which it already does [in beta behind a flag](https://docs.flutter.dev/desktop#beta-snapshot-in-stable-channel)), it has to run well for languages and cultures around the world and for people of varying kinds of abilities. It’s not quite where we want to be yet, but it’s getting there! + +One example of the ongoing work to prepare desktop for a stable release is [the complete re-architecture of how Flutter handles keyboard events to allow for synchronous response](http://flutter.dev/go/handling-synchronous-keyboard-events). This enables a widget to handle a keystroke and cancel its propagation through the rest of the tree. [The initial work landed](https://github.com/flutter/flutter/issues/33521) in Flutter 2.5 and Flutter 2.8 adds fixes for issues and regressions as it approaches stable quality. This is in addition to the ongoing work to re-engineer [how we handle device-specific keyboard input](https://github.com/flutter/flutter/issues/44918) and [a refactor of the way that Flutter handles text editing](https://github.com/flutter/flutter/pull/86736), all of which are necessary for keyboard input-intensive desktop apps. + +In addition, [we continue to expand Flutter’s support for visual density](https://github.com/flutter/flutter/pull/89353) and [expose alignment for dialogs](https://github.com/flutter/flutter/pull/88984), both to enable more desktop-friendly UI. + +<DashImage figure src="images/0SBoQZBrfuLVhEkdW.webp" /> + + +And finally, the Flutter team is not the only one working on Flutter desktop. As just one example, the desktop team at Canonical is working with Invertase on a Linux and Windows implementation of the most popular Firebase plugins for Flutter. + +<DashImage figure src="images/0xt35ZmY8r4BlFvfx.webp" /> + + +You can [read more about the preview release on the Invertase blog](https://invertase.io/blog/announcing-flutterfire-desktop). + +## DartPad + +A Flutter release is not complete without a look at the tooling improvements. This post focuses on the improvements in DartPad, the biggest of which is the support for a larger number of packages. In fact, 23 packages are available for import. In addition to several Firebase services, the list includes such popular packages as `bloc`, `characters`, `collection`, `google_fonts`, and `flutter_riverpod`. The DartPad team continues to add new packages, so if you’d like to see which packages are currently supported, click on the information icon in the lower right hand corner. + +<DashImage figure src="images/0gE84QAGGECM-tFAH.webp" /> + + +To learn the plan for adding new packages to DartPad over time, check out [this article on the Dart wiki](https://github.com/dart-lang/dart-pad/wiki/Adding-support-for-a-new-package). + +There’s another new DartPad feature that’s pretty handy, too. Previously, DartPad always ran the latest stable version. With this release, you can select the latest beta channel releases as well as the previous stable release (called the “old channel”), by using the new **Channel** menu in the status bar. + +<DashImage figure src="images/0ax1aVB0JnPAllO4a.webp" /> + + +This can be very useful if, for example, you’re writing a blog post where the latest stable isn’t quite new enough… + +## Removing the dev channel + +A Flutter “channel” governs how quickly the underlying Flutter framework and engine changes on your development machine, with stable representing the least churn and master representing the most. Due to resource constraints, we recently stopped updating the `dev` channel. While we did get some questions about this, we found that fewer than 3% of Flutter developers use the `dev` channel. We have therefore decided to start the process to formally decommission the `dev` channel. While few developers use the dev channel, it takes Flutter engineers considerable time and effort to maintain it. If you spend all of your time on the stable channel (as more than 90% of Flutter developers do), you won’t miss it. By dropping it, you have one fewer decision to make, and the Flutter team can spend its time and energy on other things. + +You can decide which channel you’d like using the `flutter channel` command. Here’s how the Flutter team thinks about each channel: + +* The `stable` channel represents the highest-quality builds we have. They’re released quarterly (roughly), and are hotfixed for critical issues in between. This is the “slow” channel: safe, mature, long-term servicing. + +* The `beta` channel provides a fast-moving alternative to stable for those who are comfortable with a faster cadence. Currently released monthly, stabilized prior to release. This is the “fast” channel. If we find that the `dev` channel was serving needs that the beta channel cannot currently serve, we may change how we think of the `beta` channel to address those needs as well (for example, accelerating the release cadence for `beta` or reducing the level of testing and hot fixing that we perform on `beta`). + +* The `master` channel is our active development channel. We provide no support on this channel, but we run a comprehensive suite of unit tests against it. This is the right channel for contributors or advanced users who are comfortable with unstable builds. On this channel, we run fast and break things (and then fix them pretty fast, too). + +As we decommission the `dev` channel in the coming months, consider the `beta` or `master` channels, depending on your tolerance for churn and your need for the latest and greatest. + +## Breaking Changes + +As always, we strive to reduce the number of breaking changes in each release. In this release, Flutter 2.8 is free of breaking changes aside from deprecated APIs that have expired and been removed according to [our breaking change policy](https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes). + +* [90292](https://github.com/flutter/flutter/pull/90292) Remove `autovalidate` deprecations + +* [90293](https://github.com/flutter/flutter/pull/90293) Remove `FloatingHeaderSnapConfiguration.vsync` deprecation + +* [90294](https://github.com/flutter/flutter/pull/90294) Remove [`AndroidViewController.id`](http://androidviewcontroller.id/) deprecation + +* [90295](https://github.com/flutter/flutter/pull/90295) Remove `BottomNavigationBarItem.title` deprecation + +* [90296](https://github.com/flutter/flutter/pull/90296) Remove deprecated text input formatting classes + +If you’re still using these APIs and would like details on how to update your code, you can [read the migration guidance on flutter.dev](https://docs.flutter.dev/release/breaking-changes/2-5-deprecations). As always, many thanks to the community for [contributing tests](https://github.com/flutter/tests/blob/master/README.md) that help us identify these breaking changes. + +## Summary + +As we close out 2021 and look forward to 2022, the Flutter team would like to express our gratitude for the work and support of the entire Flutter community. While it’s true that we’re building Flutter for the growing number of developers in the world, it’s also literally true that we couldn’t build it without you. The Flutter community is like no other and we’re grateful for all that you do. Have a happy holiday season and we’ll see you in the new year! \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/images/14ibBYWIZH1FhDIpaw0BDFg.webp b/sites/www/content/blog/whats-new-in-flutter-3-10/images/14ibBYWIZH1FhDIpaw0BDFg.webp new file mode 100644 index 0000000000..29aea2a961 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-10/images/14ibBYWIZH1FhDIpaw0BDFg.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/images/154RCRghfN9g3tTXzl0U9ew.gif b/sites/www/content/blog/whats-new-in-flutter-3-10/images/154RCRghfN9g3tTXzl0U9ew.gif new file mode 100644 index 0000000000..6d268f9228 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-10/images/154RCRghfN9g3tTXzl0U9ew.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/images/167iesripk2_62VSr5US-Iw.webp b/sites/www/content/blog/whats-new-in-flutter-3-10/images/167iesripk2_62VSr5US-Iw.webp new file mode 100644 index 0000000000..ddf47f1188 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-10/images/167iesripk2_62VSr5US-Iw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/images/16cpF7kqYEBCHqv-YswfLhg.gif b/sites/www/content/blog/whats-new-in-flutter-3-10/images/16cpF7kqYEBCHqv-YswfLhg.gif new file mode 100644 index 0000000000..050674a296 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-10/images/16cpF7kqYEBCHqv-YswfLhg.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/images/180sFA4-LrCTojCDvUJ9j2w.webp b/sites/www/content/blog/whats-new-in-flutter-3-10/images/180sFA4-LrCTojCDvUJ9j2w.webp new file mode 100644 index 0000000000..b1b3b0417a Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-10/images/180sFA4-LrCTojCDvUJ9j2w.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/images/1CPv57Dp1cYXPz-YtZ2jMcA.webp b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1CPv57Dp1cYXPz-YtZ2jMcA.webp new file mode 100644 index 0000000000..1d67a56972 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1CPv57Dp1cYXPz-YtZ2jMcA.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/images/1Fzkz6aIl-ZIry-vGojaM9w.webp b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1Fzkz6aIl-ZIry-vGojaM9w.webp new file mode 100644 index 0000000000..49f2315382 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1Fzkz6aIl-ZIry-vGojaM9w.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/images/1LyQyiGe8oQrQkQByS1PK5Q.webp b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1LyQyiGe8oQrQkQByS1PK5Q.webp new file mode 100644 index 0000000000..83fe7c938a Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1LyQyiGe8oQrQkQByS1PK5Q.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/images/1QFzoK7Gj3Q6uFopP7dOJhw.gif b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1QFzoK7Gj3Q6uFopP7dOJhw.gif new file mode 100644 index 0000000000..33c3b7f3d7 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1QFzoK7Gj3Q6uFopP7dOJhw.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/images/1SzGgItEwmTCzwMRTgnTy5A.webp b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1SzGgItEwmTCzwMRTgnTy5A.webp new file mode 100644 index 0000000000..744c900fdd Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1SzGgItEwmTCzwMRTgnTy5A.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/images/1fOYGagsonbND_VnkasWmzw.webp b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1fOYGagsonbND_VnkasWmzw.webp new file mode 100644 index 0000000000..1d86328c2f Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1fOYGagsonbND_VnkasWmzw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/images/1jYLsooI9kMMCqibaOgwwFA.gif b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1jYLsooI9kMMCqibaOgwwFA.gif new file mode 100644 index 0000000000..9e6d14f1c6 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1jYLsooI9kMMCqibaOgwwFA.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/images/1m8HdyPAVFESHa3dpiEU41g.webp b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1m8HdyPAVFESHa3dpiEU41g.webp new file mode 100644 index 0000000000..4601386cd0 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1m8HdyPAVFESHa3dpiEU41g.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/images/1n2OloNCiXMyvmFrLOk_WBw.webp b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1n2OloNCiXMyvmFrLOk_WBw.webp new file mode 100644 index 0000000000..6055268fa5 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1n2OloNCiXMyvmFrLOk_WBw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/images/1sApyn1E7o2DqWJx4fG6T8w.webp b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1sApyn1E7o2DqWJx4fG6T8w.webp new file mode 100644 index 0000000000..50e1ee116b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1sApyn1E7o2DqWJx4fG6T8w.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/images/1sqmvK_z8gkQw-eFNur6PIQ.gif b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1sqmvK_z8gkQw-eFNur6PIQ.gif new file mode 100644 index 0000000000..1cfbf7910d Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1sqmvK_z8gkQw-eFNur6PIQ.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/images/1tSZSZ2DVOT3CzX2OjxTg2Q.webp b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1tSZSZ2DVOT3CzX2OjxTg2Q.webp new file mode 100644 index 0000000000..e527df1b86 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1tSZSZ2DVOT3CzX2OjxTg2Q.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/images/1x52LrlHvIpARkrF1RevMeA.webp b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1x52LrlHvIpARkrF1RevMeA.webp new file mode 100644 index 0000000000..37087c9bee Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-10/images/1x52LrlHvIpARkrF1RevMeA.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-10/index.md b/sites/www/content/blog/whats-new-in-flutter-3-10/index.md new file mode 100644 index 0000000000..00c9769d67 --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-3-10/index.md @@ -0,0 +1,367 @@ +--- +title: "What’s new in Flutter 3.10" +description: "Seamless web and mobile integration, breakthrough graphics performance via Impeller in stable, and more" +publishDate: 2023-05-10 +author: itsjustkevin +image: images/1CPv57Dp1cYXPz-YtZ2jMcA.webp +category: releases +layout: blog +--- + +Welcome to Flutter 3.10! We can’t wait to show off the huge efforts of our amazing Flutter community. This Flutter release runs the incredible [Dart 3](https://medium.com/dartlang/announcing-dart-3-53f065a10635) also available *today*! + +Flutter 3.10 includes many improvements to web, mobile, graphics, security and so much more. Let’s get down to business! + +## Framework + +### Material 3 + +The Material library now matches the latest [Material Design spec](https://m3.material.io/components). Changes include new components and component themes, updated component visuals and more. Developers must “opt in” to these changes using the `useMaterial3` theme flag. *In the next stable release, `useMaterial3` defaults to `true`.* + +To opt in to the M3 version of the Material library, set `useMaterial3 : true` in your `MaterialApp` theme. The `flutter create` command adds this to your theme when creating a new app. + +To preview these changes, check out the [demo app](https://flutter.github.io/samples/material_3.html). The demo lets you toggle `useMaterial3` on and off. + +### ColorScheme.fromImageProvider() + +All M3 components configure the default colors of the theme’s `ColorScheme`. The default color scheme uses shades of purple. You can create a custom color scheme either from a single “seed” color or from an image. Try both variations with the demo. Color schemes generated should look good and be accessible. + +<DashImage figure src="images/1CPv57Dp1cYXPz-YtZ2jMcA.webp" /> + + +### **NavigationBar** + +This provides an M3 version of `BottomNavigationBar` widget. While [M3](https://m3.material.io/components/navigation-bar/overview) uses different colors, highlighting, and elevation, it works as it did before. To override the default appearance of the `NavigationBars` widget, use the `NavigationBarTheme` widget. Though you don’t need to migrate existing apps to this component, you should use it for new apps. + +<DashImage figure src="images/1sApyn1E7o2DqWJx4fG6T8w.webp" /> + + +### NavigationDrawer + +This provides an [M3](https://m3.material.io/components/navigation-drawer/overview) destination selection widget based on the Drawer widget. `NavigationDrawer` shows a single-selection list of `NavigationDestinations` widgets. You can include other widgets in this list as well. The `NavigationDrawer` can scroll when needed. To override the default appearance of the `NavigationDrawers` widget, use the `NavigationDrawerTheme` widget. + +<DashImage figure src="images/1tSZSZ2DVOT3CzX2OjxTg2Q.webp" /> + + +### SearchBar and SearchAnchor + +These components provide predictive text for search queries. When the user enters a search query, the app computes a list of matching responses in a “search view”. The user either selects one or adjusts the query. To override the [M3](https://m3.material.io/components/search/overview) design of these components, use the `SearchBarTheme` and `SearchAnchorTheme` widgets. + +<DashImage figure src="images/1SzGgItEwmTCzwMRTgnTy5A.webp" /> + + +<DashImage figure src="images/167iesripk2_62VSr5US-Iw.webp" /> + + +### Secondary Tab Bar + +M3 allows you to create a second tier of tabbed content. To distinguish this second `TabBar`, use [`TabBar.secondary`](https://m3.material.io/components/tabs/overview#34192702-8a18-4da7-b2cf-d1579632cb96). + +<DashImage figure src="images/1Fzkz6aIl-ZIry-vGojaM9w.webp" /> + + +### DatePicker updated for [M3](https://m3.material.io/components/date-pickers/overview) + +The M3 `DatePicker` updates the colors, layout, and shape for both the calendar and textfield versions of the widget. This doesn’t change the API, but adds a new `DatePickerTheme`. + +<DashImage figure src="images/16cpF7kqYEBCHqv-YswfLhg.gif" /> + + +### TimePicker updated for [M3](https://m3.material.io/components/time-pickers/overview) + +The M3 `TimePicker`, like the `DatePicker`, updates the colors, layout, and shapes for both the regular and compact versions of the widget. + +<DashImage figure src="images/1jYLsooI9kMMCqibaOgwwFA.gif" /> + + +### BottomSheet updated for [M3](https://m3.material.io/components/bottom-sheets/overview) + +Beyond M3 color and shape updates, the bottom sheet now adds an optional drag handle that’s included when you set `showDragHandle` to `true`. + +<DashImage figure src="images/1x52LrlHvIpARkrF1RevMeA.webp" /> + + +### ListTile updated for [M3](https://m3.material.io/components/lists/overview) + +The M3 `ListTile` updates positioning and spacing of this widget. This includes content padding, leading and trailing widget alignment, minimum leading width, and vertical spacing. The API remains unchanged. + +<DashImage figure src="images/154RCRghfN9g3tTXzl0U9ew.gif" /> + + +### Drawer updated for M3 + +The M3 `Drawer` updates the colors and elevation while making some small layout changes. + +## TextField Updates + +M3 updates all `TextField` widgets to support native gestures. Double clicking or triple clicking with a mouse work the same as double tapping or triple tapping on a touch device. By default, the `TextField` and `CupertinoTextField` widgets use these features. + +### **`TextField` double click/tap gestures** + +* **Double click + drag:** Extends the selection in word blocks. + +* **Double tap + drag:** Extends the selection in word blocks. + +<DashImage figure src="images/1QFzoK7Gj3Q6uFopP7dOJhw.gif" /> + + +### **`TextField` triple click/tap gestures** + +### Triple click + +* Selects a paragraph block at the clicked position when inside of a multi-line `TextField` (Android/Fuchsia/iOS/macOS/Windows). + +* Selects a line block at the clicked position when inside of a multi-line `TextField` (Linux). + +* Selects all text in a single-line `TextField`. + +### Triple tap + +* Selects a paragraph block at the clicked position when inside of a multi-line `TextField`. + +* Selects all text in a single-line `TextField`. + +### Triple click + drag + +* Extends the selection in paragraph blocks (Android/Fuchsia/iOS/macOS/Windows). + +* Extends the selection in line blocks (Linux). + +<DashImage figure src="images/1sqmvK_z8gkQw-eFNur6PIQ.gif" /> + + +## Flutter supports SLSA level 1 + +Flutter Framework now compiles with Supply Chain Levels for Software Artifacts ([SLSA](https://slsa.dev/)) Level 1. This acknowledges the implementation of many security features including: + +* **Scripted build process**. The build scripts for Flutter now allow automated builds on trusted build platforms. Building on protected architecture helps prevent artifact tampering which improves supply chain security. + +* **Multi-party approval with audit logging**. Flutter release workflows execute only after multiple engineers approve. All executions create auditable log records. These changes ensure that no one can introduce changes between source code and artifact generation. + +* **Provenance**. Beta and stable releases now build with [provenance](https://slsa.dev/provenance/v0.1). This means trusted sources with expected contents built the framework release artifacts. Each release publishes links to view and verify provenance on the [SDK archive](https://docs.flutter.dev/release/archive). + +<DashImage figure src="images/1n2OloNCiXMyvmFrLOk_WBw.webp" /> + + +This work also allows the team to advance toward SLSA L2 and L3 compliance. These two levels focus on protecting artifacts during and after the build process. + +## Web + +### **Flutter web apps improved load times** + +This release reduces the file size of icon fonts. It pruned unused glyphs from both Material and Cupertino. + +### **CanvasKit reduced size for all browsers** + +Chromium-based browsers can use an even smaller custom CanvasKit “flavor”. The hosted CanvasKit serves from Google’s industry-leading CDN. This should improve performance further. + +### **Element embedding** + +You can now [serve Flutter web apps from a specific element in a page](https://docs.flutter.dev/deployment/web#embedding-a-flutter-app-into-an-html-page). Before this release, your apps could either fill the entire page body or display within an `iframe` tag. [Sample code](https://github.com/flutter/samples/tree/main/web_embedding) can be found in GitHub. + +### **Shader support** + +Web apps can use Flutter’s [fragment shader support](https://docs.flutter.dev/development/ui/advanced/shaders). + +## Engine + +### Impeller + +In the 3.7 stable release, we previewed [Impeller](https://docs.flutter.dev/perf/impeller) on iOS. Since then, we received and addressed a lot of great feedback from users. With over 250 commits to Impeller in this release, we set Impeller as our default renderer on iOS. By default, all apps built for iOS with Flutter 3.10 use Impeller. These iOS apps will have less jank and better consistent performance. + +Since the 3.7 release, Impeller on iOS has improved its memory footprint. Impeller uses fewer render passes and intermediate render targets. On newer iPhones, enabling lossy texture compression reduced memory footprint without affecting fidelity. These advances also made a marked improvement in performance on iPads. + +<DashImage figure src="images/1m8HdyPAVFESHa3dpiEU41g.webp" /> + + +Consider a complex screen like the “pull quote” screen in the [Wonderous](https://flutter.gskinner.com/wonderous/) app. Together, these improvements cut the memory footprint of those screens almost in half. The reduction in memory usage also gives a modest drop in GPU and CPU load. The Wondrous app may not register that drop in load. Its frames had rendered under budget before, but this change should extend battery life. + +Impeller also unlocks the team’s ability to deliver popular feature requests faster. One example results in support for the wider P3 color gamut on iOS. Look elsewhere in this post for a description of that feature. + +Community contributions accelerated our progress, in particular GitHub users [ColdPaleLight](https://github.com/ColdPaleLight) and [luckysmg](https://github.com/luckysmg). They authored several Impeller-related patches that improved fidelity and performance. + +While Impeller meets the rendering needs of most Flutter apps, you can opt-out of Impeller. If you choose to opt-out, consider [filing an issue on GitHub](https://github.com/flutter/flutter/issues/new/choose) to tell us why. App users may notice that Skia and Impeller render with minor differences. These differences could be bugs, so don’t hesitate to file issues. In a future release, we will remove the legacy Skia renderer for iOS to reduce Flutter’s size. + +Progress continues on a Vulkan backend for Impeller. Impeller on Android remains under active development but not ready for preview. We plan to share more about it in the near future. + +To follow along with our progress, check out [our GitHub project board](https://github.com/orgs/flutter/projects/21). + +## Performance + +This release covers more performance improvements and fixes aside from Impeller. + +### Eliminating Jank + +We want to thank open-source contributor [luckysmg](https://github.com/luckysmg). They discovered they could slash the time to get the next drawable layer from the Metal driver. To get that bonus, you need to set the `FlutterViews` background color to a non-nil value. This change eliminates low frame rates on recent iOS 120Hz displays. In some cases, it *triples* the frame rate. This helped us close over half a dozen GitHub issues. This change held such significance that we backported a hotfix into the 3.7 release. + +In the 3.7 stable release, we shifted loading of local images from the platform thread to the Dart thread to avoid delaying vsync events from the platform thread. However, [users](https://github.com/flutter/flutter/issues/121525) [noticed](https://github.com/flutter/flutter/issues/121351) that this additional work on the Dart thread also caused some jank. In this release, we moved the opening and decoding of local images from the Dart thread to a [background thread](https://github.com/flutter/engine/pull/39918). This change eliminates potential long pauses on screens with a lot of local images, while avoiding delaying vsync events. In our local testing and automated benchmarks, this change cut the load time for several simultaneous images in half. + +<DashImage figure src="images/1LyQyiGe8oQrQkQByS1PK5Q.webp" /> + + +We continue to build optimizations on top of Flutter’s new internal DisplayList structure. In this release, we added an [R-Tree based culling](https://github.com/flutter/engine/pull/38429) mechanism. This mechanism removes the processing of drawing operations much earlier in our renderer. This optimization accelerates, [for example](https://github.com/flutter/flutter/issues/92366), a custom painter whose output fails offscreen. Our [microbenchmarks](https://flutter-engine-perf.skia.org/e/?begin=1671661938&end=1671754421&keys=X789f7ff76f30f8ccc672464f335fe09b&num_commits=50&request_type=1&xbaroffset=31974) showed a decreased DisplayList processing time of up to 50%. Apps with clipped custom paints could see differing improvements. The degree of improvement depends on the complexity and number of hidden draw operations. + +### Reducing iOS startup latency + +An [inefficient strategy](https://github.com/flutter/flutter/issues/37826) for identifier lookups in app bundles increased app startup latency. This startup latency grows in proportion to the app’s size. In this release, [we fixed the bundle identifier lookup](https://github.com/flutter/engine/pull/39975). This reduced startup latency by 100ms or about 30–50% in a large production application. + +### Reducing Size + +Flutter uses `SkParagraph` as the default library for text shaping, layout, and rendering. We included a flag to fall back on the legacy `libtxt` and `minikin` libraries. As we have full confidence in `SkParagraph`, [we removed](https://github.com/flutter/engine/pull/39499) `libtxt` and `minikin` and their flag in this release. This reduces Flutter’s compressed size by 30KB. + +### Stability + +In the 3.0 release, we enabled an Android feature late in our rendering pipeline. This Android feature used advanced GPU driver features. These driver features repaint less of the screen when only one “dirty” region changes. We added this to earlier optimizations to our graphics pipeline with similar effects. Though our benchmarks results encouraged us, two issues surfaced. First, the most improved benchmark might not represent practical use cases. Second, the set of devices and Android versions that supported this GPU driver feature proved [hard to find](https://github.com/flutter/engine/pull/37493). Given the limited advances and support, we [disabled](https://github.com/flutter/engine/pull/40898) the partial repaint feature on Android. + +The feature remains enabled on iOS when using the Skia backend. We expect to [enable it with Impeller](https://github.com/flutter/flutter/issues/124526) in a future release. + +## API Improvements + +### APNG Decoder + +Flutter 3.10 addresses one of our most [promoted issues](https://github.com/flutter/flutter/issues/37247). It [adds the ability](https://github.com/flutter/engine/pull/31098) to decode `APNG` images. You can load `APNG` images with Flutter’s existing image loading APIs. + +### Image loading API improvements + +Flutter’s esteemed Director of Engineering [tvolkert](https://github.com/tvolkert) has landed improvements to `dart:ui`’s image loading APIs. We have added a [new method](https://master-api.flutter.dev/flutter/dart-ui/instantiateImageCodecWithSize.html) `instantiateImageCodecWithSize`. This supports the [use case](https://github.com/flutter/flutter/issues/118543) of loading an image that meets these three conditions: + +* an aspect ratio unknown at load time + +* a bounding box constraint + +* an original aspect ratio constraint + +An example would be when an app tries to display one image from a pool of possibilities loaded from the network. + +## Mobile + +## iOS + +### Wireless debugging + +You can now run and hot reload your Flutter iOS apps without a cable! After a successful wireless pairing of your iOS device in Xcode, you can deploy your app to that device using flutter run. If you encounter issues, verify the network icon appears next to your device under **Window > Devices** and **Simulators > Devices**. To learn more, check out our [documentation](https://docs.flutter.dev/get-started/install/macos#ios-setup). + +### Wide gamut image support + +Flutter apps on iOS can now support accurate rendering for wide gamut images. To use wide gamut support, the app must use Impeller and add the [`FLTEnableWideGamut`](https://github.com/flutter/flutter/blob/a463bb82c413af319c8b715deedd7f2c345becd6/dev/integration_tests/wide_gamut_test/ios/Runner/Info.plist#L52) flag in the `Info.plist` file. + +### Spellcheck support + +The `SpellCheckConfiguration()` widget now defaults to supporting [Apple’s spell check service](https://developer.apple.com/documentation/uikit/uitextchecker) on iOS. To use this widget, set it using the `spellCheckConfiguration` parameter in `CupertinoTextField`. + +<DashImage figure src="images/14ibBYWIZH1FhDIpaw0BDFg.webp" /> + + +### Adaptive checkbox and radio + +This release adds the `CupertinoCheckBox` and `CupertinoRadio` widgets to the `Cupertino` library. They create checkbox and radio button components that match Apple’s styling. + +The Material checkbox and radio widgets added the `.adaptive` constructors. On iOS and macOS, these constructors use the corresponding Cupertino widgets. On other platforms, they use Material widgets. + +### Refining Cupertino animations, transitions and colors + +Flutter 3.10 improved some animations, transitions, and colors to match SwiftUI. These improvements include: + +* [Updating](https://github.com/flutter/flutter/pull/122275) the `CupertinoPageRoute` transition + +* [Adding](https://github.com/flutter/flutter/pull/110127) a title magnification animation to `CupertinoSliverNavigationBar` + +* Adding several [new iOS system colors](https://github.com/flutter/flutter/pull/118971) to `CupertinoColors` + +<DashImage figure src="images/180sFA4-LrCTojCDvUJ9j2w.webp" /> + + +### PlatformView performance + +When `PlatformViews` appear on screen, Flutter [throttles the refresh rate](https://github.com/flutter/engine/pull/39172) on iOS to reduce jank. App users will notice this when the app displays animated or scrollable `PlatformViews`. + +### macOS and iOS can use shared code in plugins + +Flutter now supports the [`sharedDarwinSource`](https://docs.flutter.dev/development/packages-and-plugins/developing-packages#shared-ios-and-macos-implementations) key in the `pubspec.yaml` file for plugins. This key indicates that Flutter should share iOS and macOS code. + +```yaml +ios: + pluginClass: PathProviderPlugin + dartPluginClass: PathProviderFoundation + sharedDarwinSource: true +macos: + pluginClass: PathProviderPlugin + dartPluginClass: PathProviderFoundation + sharedDarwinSource: true +``` + + +### New resources for app extensions + +We added [documentation](https://docs.flutter.dev/development/platform-integration/ios/app-extensions) for Flutter developers to use iOS app extensions. These extensions include live activities, home screen widgets, and share extensions. + +To simplify creating home screen widgets and sharing data, we added new methods to the [`path_provider`](https://github.com/flutter/packages/pull/3450) and `homescreen_widget` plugins. + +### New resources for cross-platform design + +The documentation now includes cross platform design considerations for specific [UI components](https://docs.flutter.dev/resources/platform-adaptations#ui-components). To learn more about each of these UI components, check out the [discussions in the Flutter UX GitHub repository](https://github.com/flutter/uxr/discussions). We appreciate any input or feedback! + +## Android + +### **Android CameraX support** + +[Camera X](https://developer.android.com/training/camerax), a Jetpack library, simplifies adding rich camera functionality to your Android app. This functionality applies across a wide selection of Android Camera hardware. With this release, we add preliminary support for CameraX to the Flutter Camera plugin. This support covers the following use cases: + +* Image Capture + +* Video Recording + +* Display live camera preview + +To give it a try, opt in to using the CameraX implementation. To opt in, add the following line to your `pubspec.yaml` file. + +```yaml +Dependencies: + camera: ^0.10.4 # Latest camera version + camera_android_camerax: ^0.5.0 +``` + + +We would love your feedback as we continue to add more CameraX features then make CameraX the default implementation. + +## DevTools + +We continue to improve DevTools, the suite of performance and debugging tools for Dart and Flutter. Some highlights include: + +* The DevTools UI uses Material 3. This both modernizes the look and enhances accessibility. + +* The DevTools console supports evaluations for a running app in debug mode. Before this release, you could only do this when you paused an app. + +* An embedded [Perfetto trace viewer](https://perfetto.dev/) replaces the previous timeline trace viewer. Perfetto handles larger datasets and performs better than the legacy trace viewer. Perfetto includes more features, such as: +- Allowing you to pin threads of interest. +- Clicking and dragging to select multiple timeline events from multiple frames. +- Using SQL queries to pull specific data from the timeline events. + +<DashImage figure src="images/1fOYGagsonbND_VnkasWmzw.webp" /> + + +To learn more, check out the release notes for [DevTools 2.23.1](https://docs.flutter.dev/development/tools/devtools/release-notes/release-notes-2.23.1), [DevTools 2.22.2](https://docs.flutter.dev/development/tools/devtools/release-notes/release-notes-2.22.2), and [DevTools 2.21.1](https://docs.flutter.dev/development/tools/devtools/release-notes/release-notes-2.21.1). + +## Deprecations & Breaking Changes + +### Deprecated APIs + +Breaking changes in this release include deprecated APIs that expired after the release of v3.7. To see all affected APIs, along with additional context and migration guidance, check out [the deprecation guide for this release](https://docs.flutter.dev/release/breaking-changes/3-7-deprecations). [Dart Fix](https://docs.flutter.dev/development/tools/flutter-fix) can remedy many of these issues, including quick fixes in the IDE and bulk apply with the `dart fix` command. + +### Android Studio Flamingo upgrade + +After you upgrade Android Studio to Flamingo, you might see an error when you try to flutter run or flutter build your Flutter Android apps. This error occurs because Android Studio Flamingo updates its bundled Java SDK from 11 to 17. Gradle versions [earlier than 7.3](https://docs.gradle.org/current/userguide/compatibility.html#java) can’t run when using Java 17. We [updated](https://github.com/flutter/flutter/pull/123916) `flutter analyze --suggestions` to validate if this error occurs due to incompatibility between your Java SDK and your Gradle version. + +To learn different ways to fix this error, check out [our migration guide](https://docs.flutter.dev/go/android-java-gradle-error). + +### Window singleton deprecation + +This release deprecates the window singleton. Apps and libraries relying on it should [migrate](https://docs.flutter.dev/release/breaking-changes/window-singleton) away from it. This prepares your app for multi-window support when it launches in a future version of Flutter. + +## Thanks to the community + +As always, many thanks to the community for [contributing tests](https://github.com/flutter/tests/blob/master/README.md), they help us identify these breaking changes. To learn more, check out [our breaking change policy](https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes). + +## Wrapping it up + +As we wrap up this release, the Flutter team at Google wants to thank all the contributors who have made this release possible. Your dedication and hard work have helped to make Flutter a game-changer in the industry and continue to revolutionize application development. We encourage you to start exploring this latest stable version of Flutter to take advantage of all the amazing features it has to offer. To do so, just `flutter upgrade`! Stay tuned for more exciting updates from Flutter in the future. \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0-F0HReZy8hO7g4Ua.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0-F0HReZy8hO7g4Ua.webp new file mode 100644 index 0000000000..ea306b6cd7 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0-F0HReZy8hO7g4Ua.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/02iGWjI0i3e6CztlR.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/02iGWjI0i3e6CztlR.webp new file mode 100644 index 0000000000..1b66a1d1f0 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/02iGWjI0i3e6CztlR.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/05Z-VWLi5EB_4_s1x.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/05Z-VWLi5EB_4_s1x.webp new file mode 100644 index 0000000000..e4953753c9 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/05Z-VWLi5EB_4_s1x.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0ALRxynRXg1MkS-nB.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0ALRxynRXg1MkS-nB.webp new file mode 100644 index 0000000000..5de7993c23 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0ALRxynRXg1MkS-nB.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0EzVRz68A8GCFiuJN.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0EzVRz68A8GCFiuJN.webp new file mode 100644 index 0000000000..ceeb2f2c19 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0EzVRz68A8GCFiuJN.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0GxTex9TCfT9Hwmzg.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0GxTex9TCfT9Hwmzg.webp new file mode 100644 index 0000000000..176d8a3d5c Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0GxTex9TCfT9Hwmzg.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0H4kTD2wabl_kscFq.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0H4kTD2wabl_kscFq.webp new file mode 100644 index 0000000000..8089b4ed1b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0H4kTD2wabl_kscFq.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0HCwY_q2qHScr7iYn.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0HCwY_q2qHScr7iYn.webp new file mode 100644 index 0000000000..cf676e2a3d Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0HCwY_q2qHScr7iYn.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0HuzkGU7owliBNi5N.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0HuzkGU7owliBNi5N.webp new file mode 100644 index 0000000000..d17227c854 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0HuzkGU7owliBNi5N.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0K_zKIHXbEXlHt-Jz.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0K_zKIHXbEXlHt-Jz.webp new file mode 100644 index 0000000000..e5404b7873 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0K_zKIHXbEXlHt-Jz.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0OY76w1Hu7LqTODA9.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0OY76w1Hu7LqTODA9.webp new file mode 100644 index 0000000000..ea154e42f9 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0OY76w1Hu7LqTODA9.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0VxRhItQg2lCdN5ka.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0VxRhItQg2lCdN5ka.webp new file mode 100644 index 0000000000..d60ae6a1dc Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0VxRhItQg2lCdN5ka.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0_hbW2fQla6t5bDSg.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0_hbW2fQla6t5bDSg.webp new file mode 100644 index 0000000000..fb9345c9ad Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0_hbW2fQla6t5bDSg.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0_kpFAizvZpn1TbiX.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0_kpFAizvZpn1TbiX.webp new file mode 100644 index 0000000000..2b147bc26c Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0_kpFAizvZpn1TbiX.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0aKqjrDI5zXHm7Dza.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0aKqjrDI5zXHm7Dza.webp new file mode 100644 index 0000000000..1a3e18d6cf Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0aKqjrDI5zXHm7Dza.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0bN0QtrIRWGDMC9LJ.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0bN0QtrIRWGDMC9LJ.webp new file mode 100644 index 0000000000..fdb7f6b290 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0bN0QtrIRWGDMC9LJ.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0cmIR1i5jjDqtSf7e.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0cmIR1i5jjDqtSf7e.webp new file mode 100644 index 0000000000..0381fd362f Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0cmIR1i5jjDqtSf7e.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0eenzRMajhOc8R62L.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0eenzRMajhOc8R62L.webp new file mode 100644 index 0000000000..db1e4aabbd Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0eenzRMajhOc8R62L.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0h1eFxKZNaxJoWshN.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0h1eFxKZNaxJoWshN.webp new file mode 100644 index 0000000000..9c1ba3cd29 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0h1eFxKZNaxJoWshN.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0hduILuHCxPFxzZwT.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0hduILuHCxPFxzZwT.webp new file mode 100644 index 0000000000..5739205c4c Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0hduILuHCxPFxzZwT.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0ivWbcnIt-1Mln5Lx.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0ivWbcnIt-1Mln5Lx.webp new file mode 100644 index 0000000000..6e1d086616 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0ivWbcnIt-1Mln5Lx.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0mXhB49hbDgipj12y.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0mXhB49hbDgipj12y.webp new file mode 100644 index 0000000000..49f26c8af2 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0mXhB49hbDgipj12y.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0mr1oUAyNBl6RWCOD.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0mr1oUAyNBl6RWCOD.webp new file mode 100644 index 0000000000..3f0dbc4aa6 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0mr1oUAyNBl6RWCOD.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0veAtL_lhOkJ7kU7b.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0veAtL_lhOkJ7kU7b.webp new file mode 100644 index 0000000000..6726fa0fde Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0veAtL_lhOkJ7kU7b.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/0xDizfkUpX6Kk5wCR.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0xDizfkUpX6Kk5wCR.webp new file mode 100644 index 0000000000..9a365b1dad Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/0xDizfkUpX6Kk5wCR.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/images/1J-fzkm7jc2ORDimcTWRPxg.webp b/sites/www/content/blog/whats-new-in-flutter-3-13/images/1J-fzkm7jc2ORDimcTWRPxg.webp new file mode 100644 index 0000000000..99d537aadd Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-13/images/1J-fzkm7jc2ORDimcTWRPxg.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-13/index.md b/sites/www/content/blog/whats-new-in-flutter-3-13/index.md new file mode 100644 index 0000000000..48fa9c40c9 --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-3-13/index.md @@ -0,0 +1,351 @@ +--- +title: "What’s new in Flutter 3.13" +description: "2D scrolling, faster graphics, Material 3 updates and more" +publishDate: 2023-08-16 +author: itsjustkevin +image: images/0H4kTD2wabl_kscFq.webp +category: releases +layout: blog +--- + +<DashImage figure src="images/0H4kTD2wabl_kscFq.webp" /> + + +## What’s new in Flutter 3.13 + +### 2D scrolling, faster graphics, Material 3 updates and more + +Welcome back to our quarterly Flutter stable release, this time for Flutter 3.13! In just the three months since our last release, we have had 724 pull requests merged and 55 community members authoring their first commit to Flutter! + +Keep reading to learn about all the new additions and improvements the Flutter community has contributed to this latest release! + +## Engine + +We’ve made several improvements to Impeller — our new graphics renderer — and added new Engine APIs for foldable devices. + +## Impeller + +### iOS performance improvements + +Thanks to the high-quality feedback from Flutter users, in this release we have continued to improve the performance of Impeller on iOS. As a result of many different optimizations, the Impeller renderer on iOS now not only has lower latency (by completely eliminating shader compilation jank), but on some benchmarks also have higher average throughput. In particular, on our flutter/gallery transitions performance benchmark, average frame rasterization time is now around half of what it was with Skia. + +<DashImage figure src="images/0ivWbcnIt-1Mln5Lx.webp" /> + + +*Improvements to average frame rasterization time in the Flutter Gallery transitions performance benchmark on an iPhone 11. The time period covered is roughly the time from the 3.10 branch cut to the 3.13 branch cut.* + +This progress was thanks to these and other optimizations, including: + +* Enabled dirty region management and partial repaint ([flutter/engine#40959](https://github.com/flutter/engine/pull/40959)) + +* Implemented concurrent render pass encoding ([flutter/engine#42028](https://github.com/flutter/engine/pull/42028)) + +* Made numerous improvements to text rendering ([flutter/engine#41290](https://github.com/flutter/engine/pull/41290), [flutter/engine#41780](https://github.com/flutter/engine/pull/41780), [flutter/engine#42417](https://github.com/flutter/engine/pull/42417)) + +* Added a fast path for convex shapes to avoid expensive tessellation calls ([flutter/engine#41834](https://github.com/flutter/engine/pull/41834)) + +* Started to use compute shaders for a few operations ([flutter/engine#42192](https://github.com/flutter/engine/pull/42192)) + +* More eager culling of out-of-bounds draw operations ([flutter/engine#41606](https://github.com/flutter/engine/pull/41606)) + +### Fidelity improvements + +In 3.10 we announced that wide gamut colors were available under a flag when using Impeller. After hearing and addressing feedback from users, wide gamut colors are now the default on iOS when using Impeller. + +### Progress update on Impeller on Android + +We continue to make progress on the Vulkan backend for Impeller, however it hasn’t yet reached the level of quality where an official preview period would be useful. We want to ensure that our users’ first experience with Impeller on Android is high quality and we are not quite there yet. We hope to enter a preview period for Impeller on Android in a stable release later this year. Even though Impeller on Android isn’t quite ready for preview yet, the OpenGL and Vulkan backends have benefited from many of the backend agnostic optimizations that we’ve made to Impeller’s HAL during the past year. In particular, average frame rasterization times for Android have also improved significantly on the flutter/gallery transitions performance benchmark. Further improvements are in progress so that the preview on Android can be high quality. + +<DashImage figure src="images/0_hbW2fQla6t5bDSg.webp" /> + + +Once again, our progress was greatly accelerated by contributions from the community, in particular GitHub user [ColdPaleLight](https://github.com/ColdPaleLight), who authored several much appreciated Impeller-related patches, improving fidelity and performance, including adding [support for conical gradients](https://github.com/flutter/engine/pull/42567). + +Please continue to follow along with our progress on Impeller using the Impeller [project dashboard](https://github.com/orgs/flutter/projects/21) on GitHub. We greatly appreciate all the feedback and encourage users to continue filing fidelity and performance issues in the [issue tracker](https://github.com/flutter/flutter/issues). + +### Impeller (and Wonderous) on macOS + +In our last stable release, we announced that Impeller, a rewrite of our rendering engine, would be turned on by default for iOS. Since then, we’ve heard great feedback from customers. Now, we’re excited to announce that Impeller for macOS is available in preview. You can test Impeller and enable it in your app by following the guidance on the [Impeller page](https://docs.flutter.dev/perf/impeller#availability). + +We’re eager for you to test this out and provide feedback. The best way to help us improve Impeller for macOS is to establish baseline metrics by running your macOS app without SkSL warmup and use DevTools to find instances of [jank](https://docs.flutter.dev/tools/devtools/performance#shader-compilation) due to shader compilation. Next, test your app using Impeller — click through and check for bugs, performance improvements or performance regressions. If you notice any issues, we strongly encourage you to file them on GitHub. Be sure to include information about the device you’re running on, video recordings, and an [export of your performance trace.](https://docs.flutter.dev/tools/devtools/performance#import-and-export) + +Looking to try Impeller on macOS? [Install Wonderous from the Mac App Store](https://apps.apple.com/us/app/wonderous/id1612491897)! + +<DashImage figure src="images/0mr1oUAyNBl6RWCOD.webp" /> + + +## New engine API + +### Improved foldable support + +In order to better support foldable devices, we have added a new API to retrieve various properties of a display. The new getter [FlutterView.display](https://master-api.flutter.dev/flutter/dart-ui/FlutterView/display.html) returns a [Display](https://master-api.flutter.dev/flutter/dart-ui/Display-class.html) object. The Display object reports the physical size, the device pixel ratio, and the refresh rate of the display. Check out [setPreferredOrientations](https://master-api.flutter.dev/flutter/services/SystemChrome/setPreferredOrientations.html) for an example that uses the new API. + +## Framework + +## Material + +We’ve made a number of improvements to the Material Framework to 1) offer more platform adaptability, 2) allow for more customization, and 3) add new capabilities. + +### Character recognition in TextField + +When using TextField on iOS, users will automatically see an option to use the device camera to recognize characters and insert them into the field. + +<DashImage figure src="images/1J-fzkm7jc2ORDimcTWRPxg.webp" /> + + +This feature would not be possible without the contributions of community members [luckysmg](https://github.com/luckysmg) (Author) and [tgucio](https://github.com/tgucio) (reviewer). This feature was a 1000 line and 70 commit effort that bridged the [engine](https://github.com/flutter/engine/pull/34751) and [framework](https://github.com/flutter/flutter/pull/96637)! Thank you! + +### Platform adaptive dialog + +An adaptive constructor has been added to the AlertDialog, along with the adaptive function showAdaptiveDialog, to display either a Material or Cupertino dialog depending on the current platform. + +Now using AlertDialog.adaptive() uses the [CupertinoAlertDialog](https://api.flutter.dev/flutter/cupertino/CupertinoAlertDialog-class.html) widget on iOS: + +<DashImage figure src="images/0-F0HReZy8hO7g4Ua.webp" /> + + +And Material [AlertDialog](https://api.flutter.dev/flutter/material/AlertDialog-class.html) on Android. + +<DashImage figure src="images/0mXhB49hbDgipj12y.webp" /> + + +### CupertinoDatePicker with month and year + +Adds a monthYear mode to the CupertinoDatePicker. + +<DashImage figure src="images/0hduILuHCxPFxzZwT.webp" /> + + +### Cupertino (iOS-style) check styled radio + +The useCheckmarkStyle property has been added to CupertinoRadio. This also allows the Radio.adaptive and RadioListTile.adaptive widgets to control whether they use the checkmark style on iOS. + +<DashImage figure src="images/0cmIR1i5jjDqtSf7e.webp" /> + + +### More customization options for Material widgets + +There have been several improvements that make it easier to customize the design of the Material widgets: + +* You can now use the error property for InputDecoration (as opposed to a string) to customize the error widget that is shown on text fields: + +<DashImage figure src="images/0GxTex9TCfT9Hwmzg.webp" /> + + +* You can now add tooltips to ButtonSegment: + +<DashImage figure src="images/0HCwY_q2qHScr7iYn.webp" /> + + +* You can now customize the gap in ExpansionPanelList using the materialGapSize property + +<DashImage figure src="images/0_kpFAizvZpn1TbiX.webp" /> + + +* You can now customize the trackOutlineWidth for Switch + +<DashImage figure src="images/02iGWjI0i3e6CztlR.webp" /> + + +* You can now customize the padding with the tilePadding property on NavigationDrawer + +<DashImage figure src="images/0h1eFxKZNaxJoWshN.webp" /> + + +* You can choose how to align the tabs using the alignment property for TabBar + +<DashImage figure src="images/0ALRxynRXg1MkS-nB.webp" /> + + +### MaterialState color for chips + +[This](https://github.com/flutter/flutter/pull/128584) makes it possible to customize the color of the chips in all of the different states. + +<DashImage figure src="images/0EzVRz68A8GCFiuJN.webp" /> + + +### Elevated Chips + +FilterChip.elevated,ChoiceChip.elevated,and ActionChip.elevated variants have been added in accordance with the [Material 3 specs.](https://m3.material.io/components/chips/overview) + +<DashImage figure src="images/0VxRhItQg2lCdN5ka.webp" /> + + +### onSubmitted to SearchBar + +Allows for a different action to be initiated when a user finishes the text entry and presses the Done button on the keyboard. + +### BaseTapAndDragGestureRecognizer + +A base class has been added for a family of classes, which includes support for tap + pan (TapAndPanGestureRecognizer), and tap + horizontal drag (TapAndHorizontalDragGestureRecognizer). These classes have already been used to implement native text field gestures. However, they’re also great for other use cases — for example, scaling a widget using a double tap + vertical drag gesture. + +## App Lifecycle Changes + +### AppLifeCycleListener + +AppLifecycleListener class was [added](https://github.com/flutter/flutter/pull/123274) for listening to changes in the application lifecycle, and responding to requests to exit the application. + +<DashImage figure src="images/0bN0QtrIRWGDMC9LJ.webp" /> + + +## Scrolling + +### TwoDimensional scrolling foundation + +This release of Flutter also contains the foundation for building widgets that scroll in two dimensions, which means a bunch of new classes to build with, including: + +* [ChildVicinity](https://master-api.flutter.dev/flutter/widgets/ChildVicinity-class.html), a representation similar to an index in a one dimensional scrollview, representing the relative position of children in two dimensions. + +* [TwoDimensionalChildDelegate](https://master-api.flutter.dev/flutter/widgets/TwoDimensionalChildDelegate-class.html), similar to SliverChildDelegate with equally similar subclasses: [TwoDimensionalChildBuilderDelegate](https://master-api.flutter.dev/flutter/widgets/TwoDimensionalChildBuilderDelegate-class.html) & [TwoDimensionalChildListDelegate](https://master-api.flutter.dev/flutter/widgets/TwoDimensionalChildListDelegate-class.html) + +* [TwoDimensionalScrollView](https://master-api.flutter.dev/flutter/widgets/TwoDimensionalScrollView-class.html), an abstract base class that creates a [TwoDimensionalScrollable](https://master-api.flutter.dev/flutter/widgets/TwoDimensionalScrollable-class.html) and [TwoDimensionalViewport](https://master-api.flutter.dev/flutter/widgets/TwoDimensionalViewport-class.html), matching the same model as the one dimensional ScrollView. + +* [RenderTwoDimensionalViewport](https://master-api.flutter.dev/flutter/widgets/RenderTwoDimensionalViewport-class.html), and finally, the workhorse of laying out box children in two dimensions. + +Scrolling in two dimensions also comes with some new interactions, including diagonal scrolling. See [DiagonalDragBehavior](https://master-api.flutter.dev/flutter/widgets/DiagonalDragBehavior.html) for new interaction types, and configure them on your TwoDimensionalScrollView or TwoDimensionalScrollable. + +We conducted a user study in order to develop this foundation for developers to be able to build whatever they could imagine while scrolling in all directions. Check out an example of a simple, lazy loading, two dimensional grid implemented [in this DartPad](https://dartpad.dev/?id=4424936c57ed13093eb389123383e894) in about 200 lines of code! + +The Flutter team is already at work building two dimensional scrolling widgets on top of this framework, coming soon in the two_dimensional_scrollables package. + +### New slivers + +Flutter 3.13 brings with it a new set of slivers for composing unique scrolling effects. + +[SliverMainAxisGroup](https://master-api.flutter.dev/flutter/widgets/SliverMainAxisGroup-class.html) and [SliverCrossAxisGroup](https://master-api.flutter.dev/flutter/widgets/SliverCrossAxisGroup-class.html) both support arranging multiple slivers together. In the main axis, one effect this can create is sticky headers, allowing pinned headers to be pushed out of view as each group of slivers scrolls by. + +The cross axis grouping allows for slivers to be arranged side by side in the viewport, with (also new) widgets like [SliverCrossAxisExpanded](https://master-api.flutter.dev/flutter/widgets/SliverCrossAxisExpanded-class.html) and [SliverConstrainedCrossAxis](https://master-api.flutter.dev/flutter/widgets/SliverConstrainedCrossAxis-class.html) capable of determining the allotment of space for each grouped sliver in the cross axis. + +Also new to the sliver library is [DecoratedSliver](https://master-api.flutter.dev/flutter/widgets/DecoratedSliver-class.html), similar to [DecoratedBox](https://master-api.flutter.dev/flutter/widgets/DecoratedBox-class.html). This allows users to embellish a sliver, which could even be a sliver group, with a [Decoration](https://master-api.flutter.dev/flutter/painting/Decoration-class.html). + +See all of these new slivers in action in [this DartPad](https://dartpad.dev/?id=6e2b7245063001576a3a83adb23f1121) example. + +<DashImage figure src="images/0OY76w1Hu7LqTODA9.webp" /> + + +## Accessibility + +### Accessibility updates + +* The onOffSwitchLabels accessibility property was added for CupertinoSwitch to display I/O labels + +<DashImage figure src="images/0aKqjrDI5zXHm7Dza.webp" /> + + +* The [FocusSemanticEvent has been added. ](https://github.com/flutter/flutter/pull/126171)However, it should be used with caution as it might break a users’ expectation of how a11y focus works. + +* IconButton’s isSelected is[ now available to screen readers](https://github.com/flutter/flutter/issues/127145). + +## Platforms + +## Android + +### New support targets + +With this release, Flutter now supports targeting [Android 14/ API 34](https://developer.android.com/about/versions/14). While we are still working on a few new features in Android 14 (i.e. predictive back navigation), we have thoroughly tested this release against the new Android SDK and prioritize fixing any related issues you may find. + +## iOS + +### Reduced rotation distortion on iOS + +When an iOS screen rotates, Flutter apps would previously experience some distortion that looked different from native iOS applications. We’ve made some modifications to reduce the [distortion](https://github.com/flutter/flutter/issues/16322): + +<DashImage figure src="images/0veAtL_lhOkJ7kU7b.webp" /> + + +### Renaming Runner + +When a Flutter iOS app is created, a *Runner* Xcode project and Xcode workspace are created in the `/ios` folder. Now, you can rename the workspace or project so that you don’t end up with a list of *Runners*. + +<DashImage figure src="images/0HuzkGU7owliBNi5N.webp" /> + + +### Preparing for iOS 17 and Xcode 15 + +With the impending release of iOS 17 and Xcode 15, users who desire to develop using this toolchain will need to be on Flutter 3.13. In addition, when downloading Xcode 15, make sure you also download the iOS 17 simulator. + +## Games + +### Flutter games updates + +We launched the [Flutter casual games toolkit](http://flutter.dev/games) in 2022 with a game template, tutorials, documentation, community spaces, and GCP/Firebase/Ad credits to jumpstart game development for Flutter Developers. Since then, tens of thousands of games have been published using Flutter! Since launch, we’ve actively engaged and surveyed Flutter game developers to find out how we could improve the games toolkit. Almost all of them mentioned wanting more resources and sample code to help them better design, develop and monetize their games. + +Today, we are releasing a new update to the [Flutter Games web page](https://flutter.dev/games) with a carousel of video resources and new games to learn about while building in Flutter. We have a number of new updates to the toolkit coming in the next few months with additional resources and samples to kickstart your game development journey. + +As a first step, we partnered with AdMob in July 2023 and co-hosted an exclusive UX design and Monetization workshop dedicated to Flutter game devs. ~ 100 developers joined us via a live interactive webinar, and gave the session a 4.6/5.0 satisfaction rating. We hope to summarize the workshop content and share these insights more broadly with all of you soon. + +We are actively working on more updates, so please stay tuned! If you are already using the games toolkit, and would love to send us ideas for future improvement, please don’t hesitate to email us at [flutter-games@google.com](mailto:flutter-games@google.com). + +## Tooling + +## DevTools + +### New DevTools features + +We’ve made improvements to the performance and usability of DevTools that include: + +We added a new overflow menu on the navigation bar to handle cases when the list of tabs can’t be displayed at once. + +<DashImage figure src="images/05Z-VWLi5EB_4_s1x.webp" /> + + +<DashImage figure src="images/0eenzRMajhOc8R62L.webp" /> + + +We added a legend for class types on the Memory tab. + +<DashImage figure src="images/0K_zKIHXbEXlHt-Jz.webp" /> + + +Additionally, we made scrolling a tree table in the CPU profiler faster and smoother. In the debugger, we’ve made searching in a file, or searching for a file up to 5x faster. + +To learn more, check out the release notes for [DevTools 2.25.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.25.0), and [DevTools 2.24.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.24.0). + +## Breaking changes and deprecations + +## Breaking changes + +### Material 3 by default in the next release + +We’re excited to announce that in the **next** Flutter stable release we plan to change the ThemeData useMaterial3 default from false to true. In other words, applications will get the Material 3 colors, text styles, and other visuals, by default. + +The [Material 3 demo](https://flutter.github.io/samples/web/material_3_demo/#/) should be helpful for previewing the differences between M2 and M3. + +### Android supported platforms + +Flutter no longer supports the Android Jelly Bean API levels (16, 17, and 18). The good news is that most apps should be [migrated to this new minSdkVersion by default](https://github.com/flutter/flutter/pull/129729). + +However, if you were not migrated automatically, it could be because you made changes to your module level build.gradle, and you might need to increase the minSdkVersion manually. To update, locate the module level `build.gradle` from the root of your Flutter project. It is typically found at `<YOUR PROJECT>/android/app/build.gradle`. Bump `minSdkVersion` version to 19. If you see flutter.minSdkVersion and it’s at least 19, then your minimum is set correctly. + +Flutter plugins won’t be migrated by default, so plugin authors should update the `minSdkVersion` in the top level `build.gradle` file found at `<YOUR PLUGIN>/android/build.gradle`. + +### List of changes and migration guides + +Breaking changes in this release include deprecated APIs that expired after the release of v3.10. To see all affected APIs, along with additional context and migration guidance, see [the deprecation guide for this release](https://docs.flutter.dev/release/breaking-changes/3-10-deprecations). Many of these are supported by [Flutter Fix](https://docs.flutter.dev/development/tools/flutter-fix), including quick fixes in the IDE, and bulk apply with the dart fix command. + +As always, many thanks to the community for [contributing tests](https://github.com/flutter/tests/blob/master/README.md), they help us identify these breaking changes. To learn more, check out [Flutter’s breaking change policy](https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes). + +## Contributions + +## Flutter repository priorities and triage + +### Triage updates + +Over the past few months we have adopted a new set of definitions for our priorities (P0-P3). This brings us more in line with the definitions used by most other open source projects, and simplifies the decisions we have to make regarding how important bugs are — rather than 7 categories, we now only have 4. Hopefully this will also help us communicate more effectively with those of you who file bugs and then wonder [when things will be fixed](https://github.com/flutter/flutter/wiki/Issue-hygiene#when-will-my-bug-be-fixed)! + +We’ve also introduced a [new triage scheme](https://github.com/flutter/flutter/wiki/Triage) for our teams, which we hope will make it harder for issues to fall between the cracks. If you are active in our issue database you may see our new bot commenting and adding or removing labels: + +<DashImage figure src="images/0xDizfkUpX6Kk5wCR.webp" /> + + +This bot also integrates with [our Discord](https://discord.com/invite/N7Yshp4), helping us keep on top of what is happening on GitHub. We hope it will make us more productive in the long run, but bear with us while we get used to the new system! + +## Conclusion + +As we come to the end of this announcement, I want to acknowledge that we would not be where we are today without the efforts of our fantastic community! + +For a full list of PRs that were included in this release, please check out the [release notes and change log](https://docs.flutter.dev/release/release-notes) section for this release. + +Flutter 3.13 is available in stable today, and includes [Dart 3.1](https://medium.com/dartlang/a1f4b3a7cdda/). To get started with these newest updates, all it takes is a [`flutter upgrade`](https://docs.flutter.dev/release/upgrade). + +See you all again soon! \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/01nmVYze1uMCxvzoI.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/01nmVYze1uMCxvzoI.webp new file mode 100644 index 0000000000..7b8f4775ab Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/01nmVYze1uMCxvzoI.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/04RDi6tkvHa3R2uqa.gif b/sites/www/content/blog/whats-new-in-flutter-3-16/images/04RDi6tkvHa3R2uqa.gif new file mode 100644 index 0000000000..117c98028d Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/04RDi6tkvHa3R2uqa.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0FXlCMB0adYkH2y4u.jpg b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0FXlCMB0adYkH2y4u.jpg new file mode 100644 index 0000000000..3cc7427685 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0FXlCMB0adYkH2y4u.jpg differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0KZki5KYsfeMi01Lw.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0KZki5KYsfeMi01Lw.webp new file mode 100644 index 0000000000..786e808f7f Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0KZki5KYsfeMi01Lw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0KdmeVMUgmKWzw0-P.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0KdmeVMUgmKWzw0-P.webp new file mode 100644 index 0000000000..bc3a611da8 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0KdmeVMUgmKWzw0-P.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0LMOV8BTSB4tEIisF.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0LMOV8BTSB4tEIisF.webp new file mode 100644 index 0000000000..dc773b1f8b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0LMOV8BTSB4tEIisF.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0LbHdBtWltdB4_Kcf.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0LbHdBtWltdB4_Kcf.webp new file mode 100644 index 0000000000..020bab7992 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0LbHdBtWltdB4_Kcf.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0OxjjAlCbFFXWrrR9.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0OxjjAlCbFFXWrrR9.webp new file mode 100644 index 0000000000..d4a629d43e Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0OxjjAlCbFFXWrrR9.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0PlGmgXXrK7LBpiPR.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0PlGmgXXrK7LBpiPR.webp new file mode 100644 index 0000000000..8e43b9b664 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0PlGmgXXrK7LBpiPR.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0RAkkLR53oDQekLmN.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0RAkkLR53oDQekLmN.webp new file mode 100644 index 0000000000..2f5678842f Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0RAkkLR53oDQekLmN.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0RPu62U3N57JJqu0b.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0RPu62U3N57JJqu0b.webp new file mode 100644 index 0000000000..54234dfe08 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0RPu62U3N57JJqu0b.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0UDd-ZshZBEIfb4OA.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0UDd-ZshZBEIfb4OA.webp new file mode 100644 index 0000000000..4a4a5a1f60 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0UDd-ZshZBEIfb4OA.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0Xhow360Xn88KLAWF.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0Xhow360Xn88KLAWF.webp new file mode 100644 index 0000000000..ac591cad13 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0Xhow360Xn88KLAWF.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0YKZc4cxJjsafHgd4.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0YKZc4cxJjsafHgd4.webp new file mode 100644 index 0000000000..1aa2e72212 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0YKZc4cxJjsafHgd4.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0b1mnWiuWv-cxt7g7.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0b1mnWiuWv-cxt7g7.webp new file mode 100644 index 0000000000..5c358a8011 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0b1mnWiuWv-cxt7g7.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0b3vDhobD-p7YRw6O.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0b3vDhobD-p7YRw6O.webp new file mode 100644 index 0000000000..616dcfa1ec Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0b3vDhobD-p7YRw6O.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0cnBlQufEWMXx6AJI.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0cnBlQufEWMXx6AJI.webp new file mode 100644 index 0000000000..d770aa609b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0cnBlQufEWMXx6AJI.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0gprea_z4l0Y_mizE.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0gprea_z4l0Y_mizE.webp new file mode 100644 index 0000000000..d72904964f Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0gprea_z4l0Y_mizE.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0uGcjhY3w8eKoKwLH.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0uGcjhY3w8eKoKwLH.webp new file mode 100644 index 0000000000..3ae251a995 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0uGcjhY3w8eKoKwLH.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0v9hhy6Y9fhfIbejZ.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0v9hhy6Y9fhfIbejZ.webp new file mode 100644 index 0000000000..2f4d752e0a Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0v9hhy6Y9fhfIbejZ.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0wlSYQiElLRGgOTmO.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0wlSYQiElLRGgOTmO.webp new file mode 100644 index 0000000000..caf18f7c21 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0wlSYQiElLRGgOTmO.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0wyMXhtcLW98BTKl-.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0wyMXhtcLW98BTKl-.webp new file mode 100644 index 0000000000..e318bc172f Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0wyMXhtcLW98BTKl-.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/0yoTgJmfOTF1NrHyf.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0yoTgJmfOTF1NrHyf.webp new file mode 100644 index 0000000000..07133bbabc Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/0yoTgJmfOTF1NrHyf.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/1TDndNB8cS95g5faXBKitHA.webp b/sites/www/content/blog/whats-new-in-flutter-3-16/images/1TDndNB8cS95g5faXBKitHA.webp new file mode 100644 index 0000000000..6932d28d8b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/1TDndNB8cS95g5faXBKitHA.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/images/1yWHI-1YwQAFUIVFvr5jGHQ.gif b/sites/www/content/blog/whats-new-in-flutter-3-16/images/1yWHI-1YwQAFUIVFvr5jGHQ.gif new file mode 100644 index 0000000000..536fc0e71d Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-16/images/1yWHI-1YwQAFUIVFvr5jGHQ.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-16/index.md b/sites/www/content/blog/whats-new-in-flutter-3-16/index.md new file mode 100644 index 0000000000..b91bc0e3c2 --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-3-16/index.md @@ -0,0 +1,403 @@ +--- +title: "What’s new in Flutter 3.16" +description: "Material 3 by default, Impeller preview for Android, DevTools extensions, and much more" +publishDate: 2023-11-15 +author: itsjustkevin +image: images/1TDndNB8cS95g5faXBKitHA.webp +category: releases +layout: blog +--- + +<DashImage figure src="images/1TDndNB8cS95g5faXBKitHA.webp" /> + + +## What’s new in Flutter 3.16 + +### Material 3 by default, Impeller preview for Android, DevTools extensions, and much more + +Welcome back to the quarterly Flutter stable release, this time for Flutter 3.16. This release sets Material 3 as the new default theme, brings a preview of Impeller to Android, allows adding extensions for DevTools, and much more! It also coincides with [significant updates to the Flutter Casual Games Toolkit](https://medium.com/flutter/building-your-next-casual-game-with-flutter-716ef457e440)! + +In only three months since our last release, we’ve had 928 pull requests merged by 145 community members with 40 community members authoring their first commit to Flutter! + +Keep reading to learn about all the new additions and improvements the Flutter community has contributed to this latest release! + +## Framework + +### Material 3 is the new default + +As of the 3.10 release (May 2023) the Material library has been updated to match the latest Material Design spec. Changes include new components and component themes, and updated component visuals. Prior to the 3.16 release, the changes were “opt-in” with the `useMaterial3` theme flag. As of this release, `useMaterial3` is true by default. ([#130764](https://github.com/flutter/flutter/pull/130764)) + +You can still opt out of the M3 version of the Material library by specifying `useMaterial3: false` in your `MaterialApp` theme, but Material 2 will eventually be deprecated and removed. Also, some of the widgets couldn’t merely be updated, but needed a whole new implementation. For this reason, your UI might look a little strange when you see it running as Material 3. To fix this, manually migrate to the new widgets, such as [NavigationBar](https://api.flutter.dev/flutter/material/NavigationBar-class.html). For more details, check out the [Material 3 umbrella issue](https://github.com/flutter/flutter/issues/91605) on GitHub. + +A [demo application](https://flutter.github.io/samples/material_3.html) allows you to try all of the components. The demo supports toggling `useMaterial3` so that you can compare the changes. + +The appearance of Material 3 components are primarily determined by the values for [`ThemeData.colorScheme`] and [`ThemeData.textTheme`]. The preferred way to create a Material 3 color scheme is with `ColorScheme.fromSeed()`, which creates dark and light schemes that are both aesthetically pleasing and compliant with the contrast requirements for accessibility. You can also create a color scheme from the dominant colors in an image with `ColorScheme.fromImageProvider`. To further customize the appearance of Material 3 components, add component themes to your `ThemeData`, such as [`ThemeData.segmentedButtonTheme`](https://api.flutter.dev/flutter/material/SegmentedButtonThemeData-class.html) or [`ThemeData.snackBarTheme`](https://api.flutter.dev/flutter/material/SnackBarThemeData-class.html). Non-null component theme properties override the defaults specified in the component’s API documentation. + +<DashImage figure src="images/0LMOV8BTSB4tEIisF.webp" /> + + +### Support Material 3 motion + +Improvements to Material 3 motion includes the addition of `Easing` and `Durations` classes. Material 2 curves have been renamed to include the word “legacy” and will eventually be deprecated and removed. ([#129942](https://github.com/flutter/flutter/pull/129942)) + +### Add additional options in edit menu + +On native iOS, users can now select text and initiate a share menu that provides several standard services. In this release, we’ve added the look up, search, and share options. + +<DashImage figure src="images/0YKZc4cxJjsafHgd4.webp" /> + + +### TextScaler for specifying a global text scale factor + +To support Android 14’s [non-linear font scaling feature](https://blog.google/products/android/android-14/#:~:text=Also%2C%20you%20can%20improve%20readability,rate%20than%20smaller%20font%20size.) that helps those who are visually impaired, the new [`TextScaler`](https://api.flutter.dev/flutter/painting/TextScaler-class.html) class replaces the `Text.textScaleFactor` property. ([#128522](https://github.com/flutter/flutter/pull/128522)) + +### SelectionArea updates + +Flutter’s `SelectionArea` is updated to support native gestures associated with a single or double click using a mouse, and long pressing on a touch device. These new gestures are available by default with `SelectionArea` and `SelectableRegion`. + +* Single click: Sets the collapsed selection at the clicked position. + +* Double click: Select the word at the clicked position. + +* Double click + drag: Extends the selection in word blocks. + +<DashImage figure src="images/04RDi6tkvHa3R2uqa.gif" /> + + +* Long press + drag: Extends the selection in word blocks. + +<DashImage figure src="images/0Xhow360Xn88KLAWF.webp" /> + + +### Menu items that operate on the focused widget + +This release features cleanup of focus changes when using menu items: The `FocusManager`’s `applyFocusChangesIfNeeded` function now restores menu focus — when the user clicks a menu item, focus will already have returned to the item that had focus before the menu was opened. ([#130536](https://github.com/flutter/flutter/pull/130536)) + +### Automatic reordering of menu item shortcuts for iOS, macOS + +Flutter apps on Mac platforms now order the shortcut modifiers in menus to follow Apple Human Interface Guidelines. ([#129309](https://github.com/flutter/flutter/pull/129309)) + +<DashImage figure src="images/0b1mnWiuWv-cxt7g7.webp" alt="Before" caption="Before" /> + + +<DashImage figure src="images/0LbHdBtWltdB4_Kcf.webp" alt="After" caption="After" /> + + +### MatrixTransition animation + +A new `MatrixTransition` widget allows transforms when creating animated transitions. Based on the current animation value, you can provide a matrix transformation that is applied to the child widget to create effects like the one in the following GIF. ([Example in DartPad](https://dartpad.dev/?channel=beta&id=c82be58209035722f2cc7d78da855329)) ([#131084](https://github.com/flutter/flutter/pull/131084)) + +### PaintPattern added to flutter_test + +In the `flutter_test` package, the new `PaintPattern` class allows you to validate paint calls made to the canvas by widgets like `CustomPainter`s and `Decoration`s (used in unit tests). + +Previously, a golden file was needed to verify that the correct color and rect were painted, but you can now use `PaintPattern`. The following example verifies that `MyWidget` painted a circle on the canvas: + +```dart +expect( + find.byType(MyWidget), + paints + ..circle( + x: 10, + y: 10, + radius: 20, + color: const Color(0xFFF44336), + ), +); +// Multiple paint calls can even be chained together. +expect( + find.byType(MyWidget), + paints + ..circle( + x: 10, + y: 10, + radius: 20, + color: const Color(0xFFF44336), + ), + ..image( + image: MyImage, + x: 20, + y: 20, + ), +); +``` + + +This API previously existed deep in the framework test files, and we noticed some developers found it so useful, they had copied it into their own projects. If this applies to you, you can delete the copy from your project after upgrading to Flutter 3.16. + +### Scrolling updates + +After the initial release of the two dimensional scrolling foundation in Flutter 3.13, this release brings more features and polish. With Flutter 3.16, the 2D foundation now supports `KeepAlive` widgets, as well as default focus traversal and implicit scrolling. 2D scrolling widgets built on top of the foundation will automatically adopt this support. For a comprehensive guide on building on the 2D foundation, see [the latest episode of the Flutter Build Show](https://www.youtube.com/watch?v=ppEdTo-VGcg). + +Soon after the 3.13 release, the [two_dimensional_scrollables](https://pub.dev/packages/two_dimensional_scrollables) package was published. This package is maintained by the Flutter team, and contains the first of several 2D scrolling widgets built on the framework’s foundation — `TableView`. Since the initial release, more decoration and styling support has been added with additional bug fixes. + +## Engine + +### Impeller + +### **Android** + +In this release, we are pleased to share that Impeller on Android is ready for preview feedback on the stable channel. Since early this year, the team has been hard at work on a Vulkan backend for Impeller, and this preview includes gathering feedback about Impeller’s characteristics on Vulkan-capable devices. + +<DashImage figure src="images/0wyMXhtcLW98BTKl-.webp" alt="*This chart shows improvements over the past year in 99%-ile, 90%-ile, and average frame rasterization times on the Flutter Gallery transition performance benchmark running on Impeller’s Vulkan backend, which users will observe as both less jank and higher steady state framerates. Towards the end of this period, we switched from Samsung S10 devices to Pixel 7 Pro devices, which accounts for a substantial drop in the numbers. Nevertheless, we find the trend and overall results to be very encouraging.*" caption="*This chart shows improvements over the past year in 99%-ile, 90%-ile, and average frame rasterization times on the Flutter Gallery transition performance benchmark running on Impeller’s Vulkan backend, which users will observe as both less jank and higher steady state framerates. Towards the end of this period, we switched from Samsung S10 devices to Pixel 7 Pro devices, which accounts for a substantial drop in the numbers. Nevertheless, we find the trend and overall results to be very encouraging.*" /> + + +Impeller isn’t yet expected to perform well on devices without Vulkan support. As we bring Impeller’s OpenGL backend up to feature completeness in the coming months, we plan to also seek feedback about Impeller’s characteristics on such devices in a future stable release. + +Flutter developers can try out Impeller on Vulkan-capable Android devices by passing the `— enable-impeller` flag to `flutter run`, or by adding the following setting to their project’s `AndroidManifest.xml` file under the `<application>` tag: + +```bash +<meta-data + android:name="io.flutter.embedding.android.EnableImpeller" + android:value="true" /> +``` + + +To determine whether a device supports Vulkan, Impeller uses the tests discussed in the [Impeller repo docs](https://github.com/flutter/engine/blob/main/impeller/docs/android.md#rendering-backend-selection). Generally, Impeller uses the Vulkan backend on devices running a 64-bit OS at Android API level 29 or newer. Users can also determine whether their device supports Vulkan by following the advice in [checking for Vulkan support](https://docs.vulkan.org/guide/latest/checking_for_support.html#_android). + +While confident in the progress we’ve made on Impeller’s Vulkan backend so far, there are a few known issues going into the preview period: + +* Platform views aren’t yet implemented, and frames that would otherwise include a platform view perform somewhat poorly. + +* Custom shaders are not yet implemented. + +* For the full list of [known bugs](https://github.com/orgs/flutter/projects/21/views/1?filterQuery=-status%3A%22%E2%9C%85+Done%22+status%3A%22%F0%9F%90%9E+Bugs%22) and [missing features](https://github.com/orgs/flutter/projects/21/views/11), the most up-to-date information is on the Impeller [project board](https://github.com/orgs/flutter/projects/21) on GitHub. Many issues already have fixes coming in the 3.17 beta, so please give it a try as well. + +We are generally happy with our progress on Impeller’s fidelity and performance on the Vulkan capable Android devices that we’ve tried. However, compared with iOS, the Android hardware ecosystem is much more diverse, and we anticipate a longer preview period for Android than for iOS before making it the default backend on the stable channel. For that reason, the most helpful feedback about Impeller should include detailed information about the specific device and Android version where issues occurred. + +Further, Impeller’s Vulkan backend enables additional debugging capabilities in “debug” builds beyond what is used with Skia, and these capabilities have additional runtime overhead. Therefore it’s important that feedback about Impeller’s performance is from a profile or release build, and should include timelines from DevTools and a comparison with the Skia backend on the same device. Finally, as always, we are very grateful for feedback that includes a small reproducible test case that demonstrates the issue. + +### Impeller performance, fidelity, and stability + +In addition to the focus on the Vulkan backend, since the beginning of the year, the team has also made a number of improvements to text performance in Impeller that benefit both Android and iOS. In particular, we have made improvements to the management of Impeller’s glyph atlas and the way that text workloads are divided across the Engine’s UI and raster threads. As a result, users will notice less jank in text heavy workloads. + +<DashImage figure src="images/0RPu62U3N57JJqu0b.webp" alt="*This chart shows a decrease in 99%-ile, 90%-ile, and average frame rasterization times in milliseconds in one of our text-heavy benchmarks on an iPhone 11 using Impeller. In particular, 90%-ile and average frame rasterization times are nearly halved.*" caption="*This chart shows a decrease in 99%-ile, 90%-ile, and average frame rasterization times in milliseconds in one of our text-heavy benchmarks on an iPhone 11 using Impeller. In particular, 90%-ile and average frame rasterization times are nearly halved.*" /> + + +The team has also been hard at work on fidelity and stability improvements for both Android and iOS, especially those reported by users. In particular, in the three months of this stable release cycle, the team has made 209 Impeller related commits to the flutter/engine repo, closing 217 issues, including 42 user reports of fidelity, stability, or performance issues. + +### Engine performance + +To support better performance on mobile devices with heterogeneous multiprocessing, we [have modified](https://github.com/flutter/engine/pull/45673) the engine so threads that are performance sensitive, such as the UI and raster threads, have affinity for a device’s more powerful cores. We observed that this change had a positive impact across a range of benchmarks and devices. In some cases the improvement was dramatic, halving or better 99%-ile or 90%-ile frame times. We anticipate that users will notice less jank after this change with both the Skia and Impeller backends on Android. The effect is less dramatic on iOS devices, where there is a smaller difference between the more powerful and less powerful cores. + +<DashImage figure src="images/0v9hhy6Y9fhfIbejZ.webp" alt="*This chart shows improvements in worst, 99%-ile, 90%-ile, and average frame build and frame rasterization times across nearly all of our Android benchmarks and devices.*" caption="*This chart shows improvements in worst, 99%-ile, 90%-ile, and average frame build and frame rasterization times across nearly all of our Android benchmarks and devices.*" /> + + +### API and fidelity improvements + +### Impeller performance overlay + +In previous releases, Flutter’s [performance overlay](https://docs.flutter.dev/perf/ui-performance#the-performance-overlay) feature wasn’t displayed with Impeller. This release fixes that issue. The performance overlay [now displays correctly](https://github.com/flutter/engine/pull/45259) with Impeller enabled. + +### Dithering is now displayed correctly + +In this release, the `Paint.enableDithering` property is set to true, and is deprecated as per [Flutter’s deprecation policy](https://docs.flutter.dev/release/compatibility-policy#deprecation-policy). Dithering is now enabled by default (developer-configurable dithering is no longer supported), and you will no longer see banding issues. For the full explanation of this change and the migration guide, see [the breaking change page](https://docs.flutter.dev/release/breaking-changes/paint-enableDithering) on docs.flutter.dev. + +<DashImage figure src="images/0yoTgJmfOTF1NrHyf.webp" alt="Before" caption="Before" /> + + +<DashImage figure src="images/0cnBlQufEWMXx6AJI.webp" alt="After" caption="After" /> + + +## Games + +### Flutter Games Toolkit + +Over the past few years, we’ve seen a growing community around casual game development. Tens of thousands of games have been published using Flutter, from simple but fun puzzles to more complex arcade games. Some of our favorites include Etermax’s [Trivia Crack](https://triviacrack.com/), Lotum’s [4 Pics 1 Word](https://flutter.dev/showcase/lotum) (word guess game), Dong Digital’s [Brick Mania](https://play.google.com/store/apps/details?id=net.countrymania.brick&hl=en) (arcade game), Onrizon’s [StopotS](https://play.google.com/store/apps/details?id=com.gartic.StopotS&hl=en) (categories game), the [retro pinball game](https://pinball.flutter.dev/) we built in Flutter for I/O, and [PUBG](https://flutter.dev/showcase/pubg-mobile) mobile who uses Flutter in their social and menu screens. + +<DashImage figure src="images/1yWHI-1YwQAFUIVFvr5jGHQ.gif" /> + + +To help game developers become more productive, today Flutter is launching a major update to its [Casual Games Toolkit.](http://flutter.dev) It is a collection of new resources to help you move from concept to launch with more genre-specific templates such as a card game, an endless runner game, and service integrations like Play Games Services, in-app purchase, ads, achievements, crashlytics and multiplayer support. To learn more, check out the [games toolkit launch post](https://medium.com/flutter/building-your-next-casual-game-with-flutter-716ef457e440) by [Zoey Fan](https://medium.com/@zoeyfan_43394). + +## Web + +### Flutter timeline events on Chrome DevTools + +Flutter Timeline events are now exposed in the performance panel of Chrome DevTools. ([#130132](https://github.com/flutter/flutter/issues/130132)) + +To learn more, check out [Debugging performance for web apps](https://docs.flutter.dev/perf/web-performance). + +<DashImage figure src="images/0UDd-ZshZBEIfb4OA.webp" /> + + +## Android + +### Mouse scroll wheel support + +While optimizing [Wonderous to meet Android’s large screen guidelines](https://medium.com/flutter/developing-flutter-apps-for-large-screens-53b7b0e17f10), one issue we discovered was that the mouse scroll wheel didn’t work well on tablets or foldables. It was [well known](https://github.com/flutter/flutter/issues/82973) that the user had to move the scroll wheel a significant amount for the screen to respond. + +With this release, scrolling with a mouse on a flutter view matches the scroll speed on Android devices. ([44724](https://github.com/flutter/engine/pull/44724)) + +<DashImage figure src="images/0wlSYQiElLRGgOTmO.webp" alt="Before" caption="Before" /> + + +<DashImage figure src="images/0KZki5KYsfeMi01Lw.webp" alt="After" caption="After" /> + + +### Predictive back navigation + +The Android 14 release included the predictive back gesture feature that allows you to use the back gesture on your device to “peek” at the home screen behind your current screen. This update brings predictive back gestures to Flutter! Check out the [migration guide](https://docs.flutter.dev/release/breaking-changes/android-predictive-back#migration-guide). + +<DashImage figure src="images/0KdmeVMUgmKWzw0-P.webp" /> + + +## iOS + +### App extensions + +Flutter can now be used to target some[ iOS app extensions](https://developer.apple.com/app-extensions/). This means that you can draw the UI for certain types of iOS app extensions using Flutter widgets. This doesn’t work for all types of app extensions because there might be limitations to the API (for example, home screen widgets) or memory. + +<DashImage figure src="images/0OxjjAlCbFFXWrrR9.webp" /> + + +Learn more and see an example for how to target the Share extension at [Adding iOS app extensions](https://docs.flutter.dev/platform-integration/ios/app-extensions) on docs.flutter.dev. + +## Package ecosystem + +### New Flutter Favorites + +We have rebooted the [Flutter Favorite](https://docs.flutter.dev/packages-and-plugins/favorites) program! For this cycle, the Flutter Ecosystem Committee designated the [flame](https://pub.dev/packages/flame), [flutter_animate](https://pub.dev/packages/flutter_animate), [flutter_rust_bridge](https://pub.dev/packages/flutter_rust_bridge), [riverpod](https://pub.dev/packages/riverpod), [video_player](https://pub.dev/packages/video_player), [macos_ui](https://pub.dev/packages/macos_ui) and [fpdart](https://pub.dev/packages/fpdart) packages as new Flutter Favorites. Congrats to all! + +Look out for new Flutter Favorites in the future. To nominate a package or plugin as a potential future Flutter Favorite, or to bring any other issues to the attention of the committee, send the committee an email at [flutter-committee@googlegroups.com](mailto:flutter-committee@googlegroups.com). + +<DashImage figure src="images/0RAkkLR53oDQekLmN.webp" /> + + +### First package ecosystem virtual summit + +In August, we held a first-time virtual summit for the package ecosystem, attended by more than 50 non-Googler and Googler contributors to [pub.dev](https://pub.dev). The goal was to bring contributors together in unconference-style discussions to plan, educate, learn, and share amongst the community. Satisfaction with the event was 100% in a post-event survey. We plan to partner with the community on similar events for the package ecosystem (in-person and virtually) in the future. Find out more in an upcoming Package ecosystem update blog post. + +### Cloud-based Google Maps styling + +Google Maps Platform offers the ability to customize the style of your map from the Map Styles page in the Google Cloud Console. This lets you create a customized experience without having to update your app code each time you make a style change. + +<DashImage figure src="images/0b3vDhobD-p7YRw6O.webp" /> + + +To use this feature in Flutter, you simply refer to your map using the Map ID set in the console: + +```dart +GoogleMap( + onMapCreated: _onMapCreated, + initialCameraPosition: const CameraPosition( + target: _kMapCenter, + zoom: 7.0, + ), + key: _key, + cloudMapId: _mapId +); +``` + + +### Camera X improvements + +In the 3.10 stable release, we added preliminary support for Camera X to the Flutter camera plugin. [Camera X](https://developer.android.com/training/camerax) is a Jetpack library that simplifies adding rich camera functionality to your Android app. + +With this release, we’ve added most of the features needed by any app that uses a camera. CameraX solves many issues that exist with the Camera 2 implementation of the plugin. + +We encourage you to use the CameraX plugin. To opt in, add the following line to your pubspec.yaml file. + +```yaml +Dependencies: + camera: ^0.10.4 # Or try the latest camera version + camera_android_camerax: ^0.5.0 +``` + + +We would love your feedback as we plan on making CameraX the default implementation in a future release. + +### macOS Video Player + +We have added macOS support to the [video player plugin](https://pub.dev/packages/video_player), allowing developers to play videos on a widget surface. + +To learn more check out the [video player plugin](https://pub.dev/packages/video_player) on [pub.dev](http://pub.dev). + +## DevTools + +### DevTools extensions + +The new [DevTools extensions framework](https://pub.dev/packages/devtools_extensions) enables: + +* package authors to build custom tooling for their package that is surfaced directly in DevTools. + +* package authors to write powerful tools that leverage existing frameworks and utilities from DevTools. + +* Dart and Flutter developers who are debugging an app with DevTools to get access to tools that are specific to their use cases (as determined by their app’s dependencies and which dependencies provide DevTools extensions). + +Thanks to the package authors of [Provider](https://pub.dev/packages/provider), [Drift](https://pub.dev/packages/drift), and [Patrol](https://pub.dev/packages/patrol), this ecosystem is already being built out and you can use the DevTools extensions for those packages today! + +<DashImage figure src="images/0gprea_z4l0Y_mizE.webp" alt="Provider" caption="Provider" /> + + +<DashImage figure src="images/0uGcjhY3w8eKoKwLH.webp" alt="Patrol" caption="Patrol" /> + + +<DashImage figure src="images/0PlGmgXXrK7LBpiPR.webp" alt="Drift" caption="Drift" /> + + +For a deep dive into DevTools extensions, check out the [Dart and Flutter DevTools Extensions announcement](https://medium.com/flutter/dart-flutter-devtools-extensions-c8bc1aaf8e5f) by [Kenzie Davisson](https://medium.com/@kenzieschmoll). + +### DevTools updates + +Some highlights for DevTools with this release are: + +* Added support for DevTools extensions + +* Added a new “Home” screen that shows a summary of your connected app. + +<DashImage figure src="images/01nmVYze1uMCxvzoI.webp" /> + + +Other improvements include: + +* Overall performance + +* Hot restart robustness + +* Text selection and copy behavior + +* Network profiler response viewer polish + +To learn more, check out the release notes for DevTools [2.26.1](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.26.1), [2.27.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.27.0), and [2.28.1](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.28.1). + +### VS Code UI discoverability + +Thanks to the amazing work by Flutter community member [DanTup](https://github.com/DanTup), the Flutter VS Code extension now has a Flutter Sidebar that gives you easy access to: + +* Open Flutter DevTools screens + +* View active debug sessions + +* View available devices + +* Create new projects + +* Hot reload and restart + +* Run Flutter Doctor -v + +* Much more + +<DashImage figure src="images/0FXlCMB0adYkH2y4u.jpg" /> + + +## Deprecations and breaking changes + +### Deprecations and breaking changes + +Breaking changes in this release include deprecated APIs that expired after the release of v3.13. To see all affected APIs, along with additional context and migration guidance, see [the deprecation guide for this release](https://docs.flutter.dev/release/breaking-changes/3-13-deprecations). Many of these are supported by [Flutter fix](https://docs.flutter.dev/development/tools/flutter-fix), including quick fixes in the IDE. Bulk fixes can be evaluated and applied with the `dart fix` command. + +As always, many thanks to the community for [contributing tests](https://github.com/flutter/tests/blob/master/README.md) — these help us identify these breaking changes. To learn more, check out [Flutter’s breaking change policy](https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes). + +In our next release, we will be planning to extend the deprecation policy to cover the `flutter_driver` package in addition to already supported packages, `flutter` and `flutter_test`. + +## Conclusion + +We called out the number of contributors at the beginning of this announcement, this is with intention. Flutter would not be the delightful and productive toolkit that it is today without the efforts of you all — our amazing community. Thank you. + +For a full list of PRs that were included in this release, check out the [release notes and change log](https://docs.flutter.dev/release/release-notes). + +Flutter 3.16 is available on the stable channel today, and includes [Dart 3.2](https://medium.com/dartlang/dart-3-2-c8de8fe1b91f). To get started with these newest updates, all it takes is a [`flutter upgrade`](https://docs.flutter.dev/release/upgrade). + +See you all again soon! \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-3-19/images/013y0iIXD9nN5wCcX.webp b/sites/www/content/blog/whats-new-in-flutter-3-19/images/013y0iIXD9nN5wCcX.webp new file mode 100644 index 0000000000..758aa7d5c3 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-19/images/013y0iIXD9nN5wCcX.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-19/images/01YoGEcLgpaFythel.webp b/sites/www/content/blog/whats-new-in-flutter-3-19/images/01YoGEcLgpaFythel.webp new file mode 100644 index 0000000000..14fc248095 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-19/images/01YoGEcLgpaFythel.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-19/images/06Of__x8KILe6U5Si.webp b/sites/www/content/blog/whats-new-in-flutter-3-19/images/06Of__x8KILe6U5Si.webp new file mode 100644 index 0000000000..efe61bbc3d Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-19/images/06Of__x8KILe6U5Si.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-19/images/0ZX3bHJdGGZwwOQHt.gif b/sites/www/content/blog/whats-new-in-flutter-3-19/images/0ZX3bHJdGGZwwOQHt.gif new file mode 100644 index 0000000000..87152164c6 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-19/images/0ZX3bHJdGGZwwOQHt.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-19/images/0m3qW6u7Q4mNfTKxN.webp b/sites/www/content/blog/whats-new-in-flutter-3-19/images/0m3qW6u7Q4mNfTKxN.webp new file mode 100644 index 0000000000..ba9ca2371b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-19/images/0m3qW6u7Q4mNfTKxN.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-19/images/0q9BjqFkxrFRfx9eP.webp b/sites/www/content/blog/whats-new-in-flutter-3-19/images/0q9BjqFkxrFRfx9eP.webp new file mode 100644 index 0000000000..bcd339b127 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-19/images/0q9BjqFkxrFRfx9eP.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-19/images/0zDY2wXaCTMankTTb.webp b/sites/www/content/blog/whats-new-in-flutter-3-19/images/0zDY2wXaCTMankTTb.webp new file mode 100644 index 0000000000..d5b0bb3155 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-19/images/0zDY2wXaCTMankTTb.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-19/index.md b/sites/www/content/blog/whats-new-in-flutter-3-19/index.md new file mode 100644 index 0000000000..2e2a6d1480 --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-3-19/index.md @@ -0,0 +1,229 @@ +--- +title: "What’s new in Flutter 3.19" +description: "Revolutionizing App Development with the Gemini API, Impeller Updates, and Windows Arm64 Support" +publishDate: 2024-02-15 +author: itsjustkevin +image: images/0ZX3bHJdGGZwwOQHt.gif +category: releases +layout: blog +--- + +<DashImage figure src="images/0ZX3bHJdGGZwwOQHt.gif" /> + + +## What’s new in Flutter 3.19 + +### Revolutionizing App Development with the Gemini API, Impeller Updates, and Windows Arm64 Support + +Today we present you with a new Flutter release, Flutter 3.19. This release brings a new Dart SDK for Gemini, a widget enabling developers to add fine-grained control to widget animations, a rendering boost with updates to Impeller, tooling to help implement deep links, Windows Arm64 support and so much more! + +The Flutter community continues to impress, merging 1429 pull requests by 168 community members, with 43 community members committing their first Flutter pull requests! + +Keep reading to learn about all the new additions and improvements the Flutter community has contributed to this latest release! + +## AI integration + +### Gemini Google AI Dart SDK beta release + +The Google AI Dart SDK has been released to beta. This enables you to build generative AI features into your Dart or Flutter app, powered by Gemini, Google’s latest family of AI models. There is now a [`google_generative_ai`](https://pub.dev/packages/google_generative_ai) package on pub.dev. Learn more about how to build with the Google AI Dart SDK in [this blog post](https://medium.com/flutter/harness-gemini-in-your-dart-and-flutter-apps-00573e560381) or jump straight into the [Dart quickstart](https://ai.google.dev/tutorials/dart_quickstart). + +<DashImage figure src="images/013y0iIXD9nN5wCcX.webp" /> + + +## Framework + +### Scrolling improvements + +Flutter used to scroll twice as fast if you dragged two fingers. You can now configure the default `ScrollBehavior` with `MultiTouchDragStrategy.latestPointer` to get number-of-fingers-agnostic scrolling behavior. For more information on this change, see the [migration guide](https://docs.flutter.dev/release/breaking-changes/multi-touch-scrolling). + +We also completed bug fixes for [`SingleChildScrollView`](https://github.com/flutter/flutter/pull/136871) and [`ReorderableList`](https://github.com/flutter/flutter/pull/136828), resolving a number of reported crashes and unexpected behavior. + +In two dimensional scrolling, we resolved an issue so now, if you drag or tap while a scroll is underway in either direction, the scroll activity stops as expected. + +The `TableView` widget in the `two_dimensional_scrollables` package has also been updated since the last release, providing more polish, adding support for merged cells, and adopting more of the new features of the 2D foundation after the last stable release of 3.16. + +### AnimationStyle + +Thanks to a [contribution](https://github.com/flutter/flutter/pull/138721) by Flutter community member [@TahaTesser](https://github.com/TahaTesser), Flutter has a new `AnimationStyle` widget that allows users to override the default animation behavior in widgets, such as `MaterialApp`, `ExpansionTile`, and `PopupMenuButton`, providing developers with the ability to override animation curves and durations. + +### SegmentedButton.styleFrom + +Flutter community member [@AcarFurkan](https://github.com/AcarFurkan) added a `styleFrom` static utility method, just like the ones provided by the other button types. This method enables quickly creating a `SegmentedButton`’s `ButtonStyle` that can be shared with other segmented buttons or used to configure the app’s `SegmentedButtonTheme`. + +### Adaptive Switch + +This adaptive component looks and feels native on macOS and iOS and has the Material Design look and feel elsewhere. It does not depend on the Cupertino library so its API is exactly the same on all platforms. + +See the [adaptive switch pull request](https://github.com/flutter/flutter/pull/130425) and the live example on the `Switch.adaptive` constructor [API page](https://api.flutter.dev/flutter/material/Switch/Switch.adaptive.html). + +### SemanticsProperties accessibility identifier + +A new accessibility identifier in `SemanticsProperties` provides an identifier for the semantic node in the native accessibility hierarchy. On Android, it appears in the accessibility hierarchy as `resource-id`. On iOS, this sets `UIAccessibilityElement.accessibilityIdentifier`. We want to thank community member [@bartekpacia](https://github.com/bartekpacia) for this change, which spanned the [engine](https://github.com/flutter/engine/pull/47961) and [framework](https://github.com/flutter/flutter/pull/138331). + +### Increased access to text widget state + +We added support for a `MaterialStatesController` in `TextField` and `TextFormField` so that you can listen to `MaterialState` changes. + +### UndoHistory stack + +We [fixed](https://github.com/flutter/flutter/pull/138674) a [problem](https://github.com/flutter/flutter/issues/130881) where the undo/redo history could disappear on Japanese keyboards. You can now modify an entry before it’s pushed to the `UndoHistory` stack. + +## Engine + +### Impeller progress + +**Android OpenGL preview** + +In the 3.16 stable release, we invited users to try out Impeller on Vulkan-enabled Android devices, covering 77% of Android devices in the field. Over the past few months, we have brought Impeller’s OpenGL backend up to feature parity with the Vulkan backend, for example, by adding [support for MSAA](https://github.com/flutter/engine/pull/47030). This means that Flutter apps on nearly all Android devices are expected to render correctly, with the exception of a small number of remaining features that are coming soon, such as custom shaders and full support for external textures. + +We request that Flutter developers upgrade to the latest stable version, and file issues about any shortcomings observed when [Impeller is enabled](https://docs.flutter.dev/perf/impeller#android). Feedback at this stage is invaluable to ensuring that Impeller is successful on Android and that we can confidently make it the default renderer in a release later this year. The Android hardware ecosystem is much more diverse than the iOS ecosystem. For that reason, the most helpful feedback about Impeller should include detailed information about the specific device and Android version where issues occurred. + +Further, as a reminder, Impeller’s Vulkan backend enables additional debugging capabilities in [debug](https://docs.flutter.dev/testing/build-modes#debug) builds beyond what is used with Skia, and these capabilities have additional runtime overhead. Therefore, it’s important to give feedback about Impeller’s performance from a [profile](https://docs.flutter.dev/testing/build-modes#profile) or [release](https://docs.flutter.dev/testing/build-modes#release) build. The bug report should include timelines from DevTools and a comparison with the Skia backend on the same device. Finally, as always, we are very grateful for feedback that includes a small reproducible test case that demonstrates the issue. + +**Roadmap** + +After rendering fidelity, our main focus in Impeller’s Android preview period is performance. We continue to make incremental gains, however a couple of larger improvements are also in progress. We expect work to take advantage of [Vulkan subpasses](https://github.com/flutter/flutter/issues/128911) to greatly improve the performance of advanced blend modes. Further, we also expect that a change in rendering strategy away from always tessellating every path on the CPU towards a [Stencil-then-cover](https://github.com/flutter/flutter/issues/137714) approach will greatly reduce Impeller’s CPU utilization on both Android and iOS. Finally, we expect that a new implementation of [Gaussian blurring](https://github.com/flutter/flutter/issues/131580) will match the throughput of the Skia implementation, and improve idiomatic use of blurring on iOS. + +### API improvements + +**Glyph Information** + +This release includes two new methods on `dart:ui`’s `Paragraph` object: `getClosestGlyphInfoForOffset`, and `getGlyphInfoAt`, which each return an object of the new type `GlyphInfo`. Check out the documentation on the new [`GlyphInfo`](https://main-api.flutter.dev/flutter/dart-ui/GlyphInfo-class.html) type. + +**GPU tracing** + +Under Impeller on Metal (iOS, macOS, Simulator) and on Vulkan-enabled Android devices, the Flutter engine now reports GPU times for each frame in the timeline in debug and profile builds. GPU frame timing can be inspected in DevTools under the “GPUTracer” heading. + +<DashImage figure src="images/0m3qW6u7Q4mNfTKxN.webp" /> + + +Please note that since non-Vulkan Android devices might misreport their support for querying GPU timing, Impeller’s GPU tracing can only be enabled with a flag set in the `AndroidManifest.xml` file on these devices. + +```dart +<meta-data + android:name="io.flutter.embedding.android.EnableOpenGLGPUTracing" + android:value="true" /> +``` + + +### Performance optimizations + +**Specialization constants** + +The team added [support for specialization constants](https://github.com/flutter/flutter/issues/119357) to Impeller. Taking advantage of this feature in Impeller’s shaders reduced uncompressed binary size of the Flutter engine by [nearly 350KB](https://flutter-flutter-perf.skia.org/e/?begin=1698877815&end=1702074996&queries=test%3Dhello_world_ios__compile&requestType=0&selected=commit%3D37892%26name%3D%252Carch%253Darm%252Cbranch%253Dmaster%252Cconfig%253Ddefault%252Cdevice_type%253DiPhone_11%252Cdevice_version%253Dnone%252Chost_type%253Dmac%252Csub_result%253Dflutter_framework_uncompressed_bytes%252Ctest%253Dhello_world_ios__compile%252C). + +**Backdrop filter speedups** + +There is much more work to do, however this release includes a couple of nice performance improvements for backdrop filters and blurs on Impeller. In particular, open source contributor [@knopp](https://github.com/knopp) [noticed](https://github.com/flutter/flutter/issues/131567#issuecomment-1678210475) that Impeller was mistakenly requesting the capability to read from the onscreen texture. [Removing this capability](https://github.com/flutter/engine/pull/47808) improved scenes that include multiple backdrop filters anywhere from 20–70% in our benchmarks, depending on complexity. + +Further, Impeller [no longer unconditionally stores the stencil buffer](https://github.com/flutter/engine/pull/47397) on every backdrop filter. Instead, any clip affecting operations are recorded, and replayed into a new stencil buffer when restoring the save layer for the backdrop filter. + +<DashImage figure src="images/06Of__x8KILe6U5Si.webp" /> + + +With this change, our benchmark of animated advanced blend modes on a Pixel 7 Pro running Impeller with the Vulkan backend improved average GPU frame times from 55ms to 16ms, and improved 90%-ile raster thread CPU times from around 110ms down to 22ms. + +## Android + +### Deeplinking web validator + +We have learned from developers that deep linking (taking users from a web URL to a specific page in a mobile app) has always been difficult to implement, and also error prone. So we first created a validation tool to help developers understand what links are incorrectly configured, and provide implementation guidance. We are very happy to share that an early version of the Flutter deeplink validator is now available! + +In this early version, the Flutter deep link validator supports web check on Android, which means validating the setup of your `assetlinks.json` file. You can open DevTools, click into the **Deep Links** tab, and import a Flutter project that contains deeplinks. The deeplinking validator tells you if your web file is configured correctly. You can refer to the deep link validation tool [testing instructions](https://docs.google.com/document/d/1fnWe8EpZleMtSmP0rFm2iulqS3-gA86z8u9IsnXjJak/edit?tab=t.0) for more information. + +We hope this tool is the first step to simplify your deep linking implementation journey. We will continue to work on providing future support for web check on iOS, and app check on both iOS and Android! + +<DashImage figure src="images/01YoGEcLgpaFythel.webp" /> + + +### Support for Share.invoke + +The default **Share** button on text fields and views was previously missing from Android, but we’ve added it in this release as part of our ongoing effort to ensure all the default context menu buttons are available on each platform. You can follow that ongoing work in [PR #107578](https://github.com/flutter/flutter/issues/107578). + +### Native assets feature + +If you’re interested in the interoperability of Flutter with functions from other languages in your Flutter code, you can now perform FFI calls through Native assets on Android as part of our [ongoing work](https://github.com/flutter/flutter/issues/129757) towards supporting Native assets. + +### Texture Layer Hybrid Composition (TLHC) mode + +Flutter 3.19 includes work that now makes Google Maps and the text input magnifier work in TLHC mode, which means better performance for your apps. If you’re using Google Maps, we encourage you to test out the changes and let us know your feedback! + +This work doesn’t include commits under the Framework or Engine, but you can see the work in [PR 5408](https://github.com/flutter/packages/pull/5408), along with the steps to test the THLC. + +### Custom system-wide text selection toolbar buttons + +Android apps can add custom text selection menu items that appear in all text selection menus (the menu that appears when you long-press on text). Flutter’s `TextField` selection menu now incorporates those items. + +## iOS + +### Flutter iOS native fonts + +Flutter text now looks a little more compact and a little more native on iOS. According to the Apple design guidelines, smaller fonts on iOS should be more spread out in order to be easier to read on mobile, while larger fonts should be more compact to not take up as much space. Before, we were incorrectly using the smaller, more spaced out font in all cases. Now, by default Flutter will use the compact font for larger text. + +<DashImage figure src="images/0q9BjqFkxrFRfx9eP.webp" /> + + +## DevTools + +### DevTools updates + +Some highlights for DevTools with this release are: + +* Added a new feature and screen in DevTools to validate deeplinks setup on Android. + +* Added an option in the **Enhance Tracing** menu for tracking platform channel activity. This is useful for apps with plugins. + +<DashImage figure src="images/0zDY2wXaCTMankTTb.webp" /> + + +* The Performance and CPU profiler screens are now made available when there is no connected app. Performance data or CPU profiles that were previously saved from DevTools can be reloaded for viewing from these screens. + +* The Flutter Sidebar in VS Code now has the ability to enable new platforms if not enabled for the current project, and the DevTools menu in the sidebar now has an option to open DevTools in an external browser window. + +To learn more, check out the release notes for DevTools, [2.29.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.29.0), [2.30.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.30.0), and [2.31.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.31.0). + +## Desktop + +### Windows Arm64 support + +Flutter on Windows is now embracing initial support for the Arm64 architecture, thanks to the commendable efforts of community member [@pbo-linaro](https://github.com/pbo-linaro). This initial support paves the way for more efficient and performant Flutter applications that run natively on Windows Arm64 devices. Although still in development, with progress trackable on GitHub issue [#62597](https://github.com/flutter/flutter/issues/62597), this move signifies a promising enhancement for Flutter developers aiming to optimize their apps for a broader range of Windows devices. + +## Ecosystem + +### Required reason privacy manifest + +Flutter now includes a privacy manifest on iOS to meet [upcoming Apple requirements](https://developer.apple.com/support/third-party-SDK-requirements/). + +### Progress of the Flutter and Dart package ecosystem + +In case you missed it, check out the blogpost from January on the [progress of the Flutter and Dart package ecosystem](https://medium.com/flutter/progress-of-the-flutter-package-ecosystem-17cded9a0703). + +## Deprecations and breaking changes + +### Dropping Windows 7 and 8 support + +As Flutter evolves, we’re excited to focus on the latest technologies by ending support for Windows 7 and 8 with Dart 3.3 and Flutter 3.19 releases. This shift, in line with Microsoft’s strategy, allows us to enhance Flutter on modern operating systems. We appreciate the adjustments required from our developers and are committed to assisting you through this transition. This move paves the way for a more secure, efficient, and feature-rich development environment on supported versions of Windows. Thank you for your understanding and adaptability as we continue to innovate together in the Flutter ecosystem. + +### Impeller dithering flag + +As noted in the release notes for the 3.16 stable release, the global flag `Paint.enableDithering` has been [removed](https://github.com/flutter/engine/pull/46745). See the [breaking change announcement](https://docs.flutter.dev/release/breaking-changes/paint-enableDithering) on the website for full details. + +### Deprecate iOS 11 + +Due to a [runtime crash](https://github.com/flutter/flutter/issues/136060) when certain networking APIs were called, Flutter no longer supports iOS 11. This means that apps built against Flutter 3.16.6 and later won’t run on those devices. + +### Deprecate auto render mode + +[Breaking changes](https://docs.flutter.dev/release/breaking-changes) in this release include deprecated APIs that expired after the release of v3.16. To see all affected APIs, along with additional context and migration guidance, see the [deprecation guide for this release](https://docs.flutter.dev/release/breaking-changes/3-16-deprecations). Many of these deprecations are supported by [Flutter fix](https://docs.flutter.dev/development/tools/flutter-fix), including quick fixes in the IDE. Bulk fixes can be evaluated and applied with the `dart fix` command line tool. + +As always, many thanks to the community for [contributing tests](https://github.com/flutter/tests/blob/master/README.md) — these help us identify these breaking changes. To learn more, check out [Flutter’s breaking change policy](https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes). + +This is the first release to adopt the flutter_driver package into the deprecation policy in addition to already supported packages, flutter and flutter_test. + +## Conclusion + +As we highlighted the remarkable number of contributors at the start of this announcement, we did so with purpose. The evolution of Flutter into the powerful and efficient toolkit it has become is a direct testament to the dedication and hard work of our incredible community. A heartfelt thank you to each and every one of you. + +To dive into the specifics of what has been achieved with this release, we invite you to view the [release notes and change log](https://docs.flutter.dev/release/release-notes/release-notes-3.19.0) for a comprehensive list of additions in Flutter 3.19. + +Flutter 3.19, alongside [Dart 3.3](https://medium.com/dartlang/new-in-dart-3-3-extension-types-javascript-interop-and-more-325bf2bf6c13), is now available on the stable channel. Embarking on this latest journey with Flutter is as straightforward as running flutter upgrade. \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-3-22/images/0-KX8Ubw77KpdGnPI.webp b/sites/www/content/blog/whats-new-in-flutter-3-22/images/0-KX8Ubw77KpdGnPI.webp new file mode 100644 index 0000000000..196fd80aea Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-22/images/0-KX8Ubw77KpdGnPI.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-22/images/08b6DxQuMXAyYVu-w.webp b/sites/www/content/blog/whats-new-in-flutter-3-22/images/08b6DxQuMXAyYVu-w.webp new file mode 100644 index 0000000000..858ec65cd1 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-22/images/08b6DxQuMXAyYVu-w.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-22/images/0KmIhzrfoyskNW7r8.webp b/sites/www/content/blog/whats-new-in-flutter-3-22/images/0KmIhzrfoyskNW7r8.webp new file mode 100644 index 0000000000..3ad5f7b0e6 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-22/images/0KmIhzrfoyskNW7r8.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-22/images/0hr_OF9DsfUcsLNHv.webp b/sites/www/content/blog/whats-new-in-flutter-3-22/images/0hr_OF9DsfUcsLNHv.webp new file mode 100644 index 0000000000..aab4777ad6 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-22/images/0hr_OF9DsfUcsLNHv.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-22/images/0mtFyiLoIUSqk_zRB.webp b/sites/www/content/blog/whats-new-in-flutter-3-22/images/0mtFyiLoIUSqk_zRB.webp new file mode 100644 index 0000000000..2a08829452 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-22/images/0mtFyiLoIUSqk_zRB.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-22/images/0uk0URkHcImHdTq2M.webp b/sites/www/content/blog/whats-new-in-flutter-3-22/images/0uk0URkHcImHdTq2M.webp new file mode 100644 index 0000000000..952ee7a2fc Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-22/images/0uk0URkHcImHdTq2M.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-22/images/0x6HEkml8cFGc96hg.webp b/sites/www/content/blog/whats-new-in-flutter-3-22/images/0x6HEkml8cFGc96hg.webp new file mode 100644 index 0000000000..407842d9ae Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-22/images/0x6HEkml8cFGc96hg.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-22/images/11lCd7dBwJ0ab_sieDlQNFw.webp b/sites/www/content/blog/whats-new-in-flutter-3-22/images/11lCd7dBwJ0ab_sieDlQNFw.webp new file mode 100644 index 0000000000..cca4db3aa5 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-22/images/11lCd7dBwJ0ab_sieDlQNFw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-22/images/1hf9CEzGh0uhxnzVZi2Kk4g.webp b/sites/www/content/blog/whats-new-in-flutter-3-22/images/1hf9CEzGh0uhxnzVZi2Kk4g.webp new file mode 100644 index 0000000000..b862129595 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-22/images/1hf9CEzGh0uhxnzVZi2Kk4g.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-22/index.md b/sites/www/content/blog/whats-new-in-flutter-3-22/index.md new file mode 100644 index 0000000000..969bedf073 --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-3-22/index.md @@ -0,0 +1,242 @@ +--- +title: "What’s new in Flutter 3.22" +description: "WebAssembly, Graphics rendering enhancements, and more options for AI integration" +publishDate: 2024-05-14 +author: itsjustkevin +image: images/1hf9CEzGh0uhxnzVZi2Kk4g.webp +category: releases +layout: blog +--- + +<DashImage figure src="images/1hf9CEzGh0uhxnzVZi2Kk4g.webp" /> + + +## What’s new in Flutter 3.22 + +### WebAssembly, Graphics rendering enhancements, and more options for AI integration + +Welcome back for another exciting Flutter stable release! This time, we’re thrilled to present Flutter 3.22. We’re bringing WebAssembly to the stable channel, a fully featured Vulkan backend for Impeller on Android, promising smoother graphics and a major performance boost. We’re also introducing streamlined workflows with new widget state properties, dynamic view sizing, and improved form validation. But that’s not all — you’ll find flavor-conditional asset bundling, a preview of Vertex AI for Firebase in Dart, and updated DevTools to make your life easier. + +In just a few months since our last update, we’ve merged an impressive 1595 pull requests from the Flutter community, with 37 new community members contributing to Flutter for the first time! + +So, dive in and discover all the new features and enhancements that the Flutter community has brought to this latest release! + +## WebAssembly + +With the release of Flutter 3.22, Wasm is now available on the stable channel, offering significant performance improvements. In our internal benchmarks using Chrome on an M1 MacBook, the Wonderous app’s frame rendering time improved by 2x on average and 3x in worst-case scenarios. + +<DashImage figure src="images/0x6HEkml8cFGc96hg.webp" /> + + +These enhancements are vital for apps with animations and rich transitions, where maintaining a smooth frame rate is essential. Wasm helps achieve this by reducing performance bottlenecks, resulting in smoother animations and transitions. To start using Wasm with your Flutter web apps, check out our [Dart Wasm documentation](https://dart.dev/web/wasm) and [Flutter Wasm documentation](https://docs.flutter.dev/platform-integration/web/wasm). For the full announcement, visit the [Flutter at Google I/O blog post](https://medium.com/flutter/io24-5e211f708a37). + +## Engine + +Flutter 3.22 introduces significant updates to Impeller, the rendering engine that powers your Flutter applications. Key highlights include the completion of the Vulkan backend on Android for smoother graphics and improved performance, ongoing optimizations for blur effects and complex path rendering, and a new experimental API for testing with Impeller. In line with our [roadmap](https://github.com/flutter/flutter/wiki/Roadmap#core-framework--engine), we’re committed to enhancing Impeller’s quality and performance, including completing the iOS migration to Impeller and expanding Android support. + +### Impeller + +### **Vulkan backend feature complete on Android** + +In this release, Impeller’s Vulkan backend for Android is feature complete. In particular, in the past few months, the team has been hard at work completing the implementation of [fast advanced blends](https://github.com/flutter/engine/pull/50154), support for custom fragment shaders with the [FragmentProgram API](https://github.com/flutter/engine/pull/49543), [PlatformView](https://github.com/flutter/engine/pull/50730) support (though it requires a [small API migration](https://docs.flutter.dev/release/breaking-changes/android-surface-plugins)), and fully implementing [all blur styles](https://github.com/flutter/flutter/issues/134178). + +### Android preview + +In the 3.19 stable release, after releasing improvements in Impeller’s OpenGL backend, we invited users to try out Impeller on Android devices both with and without Vulkan support. Over the past few months, after evaluating the performance of the OpenGL backend and estimating the remaining work on the Vulkan backend, we have decided to focus our efforts on making the Vulkan backend production ready first. + +Impeller solves the issue of shader compilation jank. Additionally, in our benchmarks it outperforms the legacy renderer on average, 90th, and 99th percentile frame times. We therefore believe that the performance of the Vulkan backend on Android is acceptable. In this release (3.22), an app that opts-in to Impeller will use the Vulkan backend where available. In a future release, this will become the default. When an app that opts-in to Impeller runs on a device that doesn’t support Vulkan, Flutter will gracefully fall back automatically to using OpenGL ES with Skia. No action is necessary on your part. In the future, when we believe the OpenGL ES Impeller backend is production ready, this fallback will also use Impeller. + +As the Impeller preview on Android continues through the 3.22 stable cycle, we request that Flutter developers upgrade to the latest stable version, and file issues about any shortcomings noticed when [Impeller is enabled](https://docs.flutter.dev/perf/impeller#android). Feedback at this stage is invaluable to ensuring that Impeller is successful on Android and that we will be able to confidently make it the default renderer in a release later this year. The Android hardware ecosystem is very diverse. For that reason, the most helpful feedback about Impeller should include detailed information about the specific device and Android version where issues occurred. + +### Blur performance improvements + +Blur has been [reimplemented](https://github.com/flutter/engine/pull/47576) in Impeller for both iOS and Android. In particular, the new approach, which is similar to Skia’s, reduces the CPU and GPU time of blurs by nearly half in [benchmarks](https://flutter-flutter-perf.skia.org/e/?begin=1699468487&end=1710262311&keys=X01fc3d52ebd6fbf38afef91d82ab8d2b&requestType=0&selected=commit%3D38815%26name%3D%252Carch%253Dintel%252Cbranch%253Dmaster%252Cconfig%253Ddefault%252Cdevice_type%253DiPhone_11%252Cdevice_version%253Dnone%252Chost_type%253Dmac%252Csub_result%253Daverage_frame_rasterizer_time_millis%252Ctest%253Dbackdrop_filter_perf_ios__timeline_summary%252C&xbaroffset=38815). + +The chart below shows worst-case, 99%-ile, 90%-ile, and average frame rasterization times and GPU frame times in ms on an iPhone 11 device in a pathological benchmark intended to highlight blur performance. After rewriting Impeller’s blur, both the CPU and GPU cost of backdrop filter blurs has been nearly halved. This scale of this improvement translates to non-pathological cases as well, as would appear in typical apps. + +<DashImage figure src="images/0mtFyiLoIUSqk_zRB.webp" alt="99%-ile, 90%-ile and average frame rasterization times and GPU frame times in ms on an iPhone 11 device in a pathological benchmark intended to highlight blur performance" caption="99%-ile, 90%-ile and average frame rasterization times and GPU frame times in ms on an iPhone 11 device in a pathological benchmark intended to highlight blur performance" /> + + +### Stencil-then-Cover + +Impeller on both iOS and Android has [moved to a new rendering strategy](https://github.com/flutter/engine/pull/51219) based on the Stencil-then-Cover approach described in the chapter “Drawing Filled, Concave Polygons Using the Stencil Buffer” in the [OpenGL Redbook](http://www.opengl-redbook.com/). Team members discussed more on this technique as it applies to Flutter in GitHub issue [#123671](https://github.com/flutter/flutter/issues/123671). + +This approach solves the issue where the raster thread was spending too much time calculating tessellations for complex paths on the CPU for example, SVGs and [Lottie animations](https://github.com/flutter/flutter/issues/141961). After the change, the total frame time (UI thread on the CPU + raster thread on the CPU + GPU work) is much lower for frames that contain complex paths. Users will notice that Lottie animations and other complex paths render more smoothly, with lower CPU utilization, and slightly higher GPU utilization. + +<DashImage figure src="images/11lCd7dBwJ0ab_sieDlQNFw.webp" alt="(Left) A Lottie animation. Previously, Impeller on a recent iPhone took 64ms / frame of raster thread CPU time to render it. (Right) The same animation on the same device after we landed the Stencil-then-Cover optimization. Raster times are nearly 10x faster." caption="(Left) A Lottie animation. Previously, Impeller on a recent iPhone took 64ms / frame of raster thread CPU time to render it. (Right) The same animation on the same device after we landed the Stencil-then-Cover optimization. Raster times are nearly 10x faster." /> + + +While pleased with these improvements, there is still more work to do. Among other opportunities, we are aware that polyline generation remains prominent in CPU profiles, and we intend to investigate shifting this work to the GPU, as well. + +### New API + +While still experimental, flutter test now accepts the `--enable-impeller` flag, which exercises Impeller using the Vulkan backend. + +## Framework + +### Widget state properties + +`MaterialState` has been moved outside of the Material library and renamed `WidgetState`, in order to make it available to Cupertino, the base Flutter framework, and package authors. For more information on migrating to the new WidgetState, see the [migration guide](https://docs.flutter.dev/release/breaking-changes/material-state). + +### Dynamic view sizing + +[Enhancements](https://github.com/flutter/flutter/pull/140918) to dynamic view sizing benefits developers building responsive layouts, ensuring better UI adaptability across various device screens. + +### Improved form validation + +Thanks to the [contributions](https://github.com/flutter/flutter/pull/135578) of Flutter community member [SharbelOkzan](https://github.com/SharbelOkzan), Flutter 3.22 comes with more flexible form validation methods allowing developers to create more robust user input handling, enhancing both usability and security. + +### Covariants in 2D APIs + +Reducing the need for type casts in 2D graphics APIs simplifies development workflows and enhances performance, important for games and complex animations. + +### Flavor-conditional asset bundling + +Developers using the [flavors](https://docs.flutter.dev/deployment/flavors) feature can now configure individual assets to be bundled only when building for a specific flavor. For more information, check out [Conditionally bundling assets based on flavor](https://docs.flutter.dev/deployment/flavors#conditionally-bundling-assets-based-on-flavor). + +### Transformation of assets using Dart packages + +Users can now configure Dart packages to transform their app’s assets as they are bundled. For more information, check out [Transforming assets at built time](http://docs.flutter.dev/ui/assets/asset-transformation). + +## Android + +### Deep linking + +Deep links can significantly improve the user experience in your Flutter app, acting as shortcuts that seamlessly guide users to specific content within your app, boosting engagement, and driving sales. While Universal Links for iOS and App Links for Android are highly recommended for their security and user-friendly nature, setting them up can be a bit tricky. + +In the last Flutter stable release, we introduced a deep link validator tool within DevTools that supports checking web configuration for Android apps. In this version, we added a new set of features to help verify setups within your Android manifest files. + +For more information on using this tool, check out [Validate deep links](https://docs.flutter.dev/tools/devtools/deep-links). + +### Predictive back gesture + +Flutter now adds more support for Android’s upcoming predictive back feature, where users can peek at the previous route or even the previous app during a back gesture. This is still behind a feature flag on Android devices, but you can find details on how to try it out yourself [on GitHub](https://github.com/flutter/flutter/issues/132504#issuecomment-2025776552). + +<DashImage figure src="images/08b6DxQuMXAyYVu-w.webp" /> + + +<DashImage figure src="images/0hr_OF9DsfUcsLNHv.webp" /> + + +### Flutter tool enforces version requirements on Gradle, AGP, Java, and Kotlin + +In this release, the Flutter tool enforces a policy regarding the versions that it supports for Gradle, the Android Gradle Plugin (AGP), Java, and Kotlin. Initially, the tool only provides warnings. + +Currently, the supported version ranges are as follows: + +* Gradle — Fully supported 7.0.2 to current, warn otherwise + +* AGP — Fully supported 7.0.0 to current, warn otherwise + +* Java — Fully supported Java 11 to current, warn otherwise + +* Kotlin — Fully supported 1.5.0 to current, warn otherwise + +In the next major release these warnings will become errors, which can be overridden with the flag `--android-skip-build-dependency-validation`. More generally speaking, the tool provides a warning for at least one release before fully dropping support (generating an error) for a given version of these dependencies. + +This policy was discussed in an [associated design spec](https://docs.google.com/document/d/1qeeM5QG-jiafttSgvc7yvC19IDRggFFZQTktBVxL6sI/edit?resourcekey=0-HLEAiBOMxAlQxDs-mEeffw). Comments and feedback are always welcome. + +### Support for using Gradle Kotlin DSL in Gradle build scripts on Android + +Gradle Kotlin DSL is now supported in Flutter, providing an alternative to the traditional Gradle Groovy DSL. This support allows for a better code editing experience, featuring auto-completion, quick access to documentation, source navigation, and context-aware refactoring. + +This initial support was contributed by GitHub user [bartekpacia](https://github.com/bartekpacia). Developers can now choose to rewrite their Gradle build scripts in Kotlin to take advantage of these benefits, although the Flutter tool doesn’t yet allow for selecting Kotlin over Groovy when using `flutter create`. + +For more details, check out the [PR 140744](https://github.com/flutter/flutter/pull/140744) by [bartekpacia](https://github.com/bartekpacia). + +### Platform views improvements + +**Heads up for all Flutter app developers**! If you’re using Flutter to build apps that rely on native Android components (like maps, web views, or certain UI elements), we have some important news. + +Due to a bug in Android 14, apps built with older versions of Flutter might not work properly on devices running this new Android version. + +Flutter 3.22 fixes this issue and improves the overall performance of these native components in your Android apps. So, to ensure your app runs smoothly on all Android devices, make sure to rebuild and release your app with Flutter 3.22. + +This update also includes behind-the-scenes improvements to make platform views on Android more reliable and performant overall. + +### End of support for KitKat + +Flutter’s minimum supported Android version is now Lollipop (API 21). Beginning with Flutter’s 3.22 stable release, Flutter will no longer work on devices running Android KitKat (API 19). For more details, see our [deprecation guide](https://docs.flutter.dev/release/breaking-changes/android-kitkat-deprecation). + +## iOS + +### Platform view performance + +We understand that platform view performance on iOS has been a pain point for many Flutter developers. This has been especially noticeable within scroll views when using platform views. + +Recent updates directly address these concerns, with significant improvements in scenarios like embedding multiple inline ads within an article. Here are some key improvements [in our benchmark](https://github.com/flutter/flutter/pull/144745): + +* **Reduced GPU usage:** GPU usage has been reduced by 50%, leading to less power consumption and a potentially smoother user experience. + +* **Improved frame rendering:** Average frame render times have decreased by 1.66ms (33%). + +* **Minimized jank:** Worst-case frame render times have been reduced by 3.8ms (21%). + +If you’ve previously experienced performance challenges when using multiple platform views (like ads, maps, etc) within scrolling views, these optimizations offer the potential for a more fluid and responsive scrolling experience. Please give it a try and let us know what you think. + +<DashImage figure src="images/0uk0URkHcImHdTq2M.webp" /> + + +<DashImage figure src="images/0-KX8Ubw77KpdGnPI.webp" /> + + +## Ecosystem + +### Vertex AI for Firebase Dart SDK preview release + +The Vertex AI for Firebase product has been released to public preview and includes the Dart SDK. This enables you to use the Gemini API to build generative AI features for your Dart or Flutter app, with production, performance and enterprise scale in mind. The SDK is integrated with [Firebase App Check](https://firebase.google.com/docs/app-check), which protects your API calls, and safeguards your backend infrastructure from serious threats like billing fraud, phishing, and app impersonation. Jump into the [Getting Started for Dart](https://firebase.google.com/docs/vertex-ai/get-started?platform=flutter) and start using it with no cost with a promo code + +<DashImage figure src="images/0KmIhzrfoyskNW7r8.webp" /> + + +The [Google AI Dart SDK](https://ai.google.dev/gemini-api/docs/get-started/dart) remains available, and is recommended for prototyping only. Google AI has free-of-charge access (within limits and where available) and pay-as-you-go pricing. If you have been prototyping with the Google AI Dart SDK, and are ready to migrate to Vertex AI for Firebase, check out the [migration guide](https://firebase.google.com/docs/vertex-ai/migrate-to-vertex-ai?platform=flutter). + +### DevTools updates + +We continue to improve DevTools, the suite of performance and debugging tools for Dart and Flutter. This release includes performance improvements, general polish, and new features like including CPU samples in the timeline, advanced filtering, and support for importing and exporting memory snapshots. + +Other notable improvements were shipped with the `devtools_extensions` and `devtools_app_shared` packages that support DevTools extension authors. We added support for connecting an extension to the new Dart Tooling Daemon (DTD), which allows DevTools extensions to access public methods registered by other DTD clients, such as an IDE, as well as allowing access to a minimal file system API for interacting with the development project. + +To learn more about all the updates included in Flutter 3.22 check out the release notes for DevTools [2.32.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.32.0), [2.33.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.33.0), and [2.34.1](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.34.1). + +### Google Mobile Ads SDK for Flutter + +For those of you monetizing your Flutter apps with Ads, we’ve got some exciting news: Google Mobile Ads for Flutter has just released a major update to version 5.0.1! + +**Enhanced support for User Messaging Platform (UMP) SDK:** The update adds support for the latest APIs from the Android UMP SDK version 2.2.0 and iOS UMP SDK version 2.4.0. The UMP SDK is crucial for complying with privacy regulations, making it easier for you to obtain user consent for personalized ads. This new version introduces several new APIs to simplify the consent gathering process. + +**Expanded mediation partners:** We’ve broadened your ad monetization horizons by offering integrations with popular ad partners, including [Unity](https://pub.dev/packages/gma_mediation_unity), [Meta](https://pub.dev/packages/gma_mediation_meta), [AppLovin](https://pub.dev/packages/gma_mediation_applovin), [Iron Source](https://pub.dev/packages/gma_mediation_ironsource), [Mintegral](https://pub.dev/packages/gma_mediation_mintegral), [Pangle](https://pub.dev/packages/gma_mediation_pangle), [DT Exchange](https://pub.dev/packages/gma_mediation_dtexchange), [InMobi](https://pub.dev/packages/gma_mediation_inmobi), and[ Liftoff.](https://pub.dev/packages/gma_mediation_liftoffmonetize) You can now maximize your app revenue with expanded mediation options and simplified implementation. + +We encourage you to try out these new features in your Flutter apps and let us know which other mediation partners you’d like to see us support. Your feedback is invaluable as we continue to enhance the Google Mobile Ads SDK for Flutter. + +## Breaking Changes and Deprecations + +### Removal of v1 Android embedding + +Deletion of version one of the Android embedding is under way. This will likely have no effect on most apps, as + +1. Version two has been the default for many years + +1. The Flutter tool would already block building version one apps, unless specifically overridden with the flag `-- ignore-deprecation`. + +This release breaks Flutter tool support for v1 apps completely. **It is no longer possible to override.** + +**Plugin authors, please note**: when the v1 android embedding was initially deprecated a migration doc was written for plugin authors at [https://docs.flutter.dev/release/breaking-changes/plugin-api-migration](https://docs.flutter.dev/release/breaking-changes/plugin-api-migration). As part of this migration, it was recommended that plugin authors keep support for apps using the v1 embedding, by including in their `*Plugin.java` a method with the signature + +`public static void registerWith(@NonNull io.flutter.plugin.common.PluginRegistry.Registrar registrar)` + +We plan to fully delete the v1 Android embedding in the next release, **at which point plugins that include a method with this signature will no longer compile** (as it makes reference to a type from the v1 android embedding). + +It currently serves no purpose, as this release has broken apps using the v1 embedding. We recommend that plugin authors release updated versions of their plugins with the v1 code removed as soon as possible, to avoid breakage in future versions of Flutter. For an example, check out [PR 6494](https://github.com/flutter/packages/pull/6494), which removed the plugins maintained by the Flutter team. + +### Deprecations removed in 3.22 + +[Breaking changes](https://docs.flutter.dev/release/breaking-changes) in this release include deprecated APIs that expired after the release of v3.19. To see all affected APIs, along with additional context and migration guidance, see the [deprecation guide for this release](https://docs.flutter.dev/release/breaking-changes/3-19-deprecations). Many of these are supported by [Flutter fix](https://docs.flutter.dev/development/tools/flutter-fix), including quick fixes in the IDE. Bulk fixes can be evaluated and applied with the `dart fix` command-line tool. + +As always, many thanks to the community for [contributing tests](https://github.com/flutter/tests/blob/master/README.md) — these help us identify these breaking changes. To learn more, check out [Flutter’s breaking change policy](https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes). + +## Conclusion + +At the heart of Flutter’s success is you — our amazing community. This release wouldn’t be possible without your countless contributions and unwavering passion. From the bottom of our hearts, thank you. + +Ready to explore Flutter 3.22? Dive into the full release notes and changelog, fire up your terminal, and run `flutter upgrade`. We can’t wait to see what you build! \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-3-24/images/07LXjXlFR9w-VR6xk.webp b/sites/www/content/blog/whats-new-in-flutter-3-24/images/07LXjXlFR9w-VR6xk.webp new file mode 100644 index 0000000000..ec95c38339 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-24/images/07LXjXlFR9w-VR6xk.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-24/images/0BzEybhMlV-SnSZZl.webp b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0BzEybhMlV-SnSZZl.webp new file mode 100644 index 0000000000..fbb0ac7135 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0BzEybhMlV-SnSZZl.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-24/images/0DwZgIybQr3FDFxXd.gif b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0DwZgIybQr3FDFxXd.gif new file mode 100644 index 0000000000..52a592f456 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0DwZgIybQr3FDFxXd.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-24/images/0QvoXCNqqfsrMn1oC.webp b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0QvoXCNqqfsrMn1oC.webp new file mode 100644 index 0000000000..9432c5a6dd Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0QvoXCNqqfsrMn1oC.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-24/images/0_GmbsIKfnwEAW6G8.gif b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0_GmbsIKfnwEAW6G8.gif new file mode 100644 index 0000000000..e4358be9e4 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0_GmbsIKfnwEAW6G8.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-24/images/0_c0s8KMdLNyfxOMQ.webp b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0_c0s8KMdLNyfxOMQ.webp new file mode 100644 index 0000000000..f6e4d38305 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0_c0s8KMdLNyfxOMQ.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-24/images/0_o_9V0RlITOZdU4v.webp b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0_o_9V0RlITOZdU4v.webp new file mode 100644 index 0000000000..cd431909c4 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0_o_9V0RlITOZdU4v.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-24/images/0bdcb6KGonosfPZ6m.webp b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0bdcb6KGonosfPZ6m.webp new file mode 100644 index 0000000000..69049dec66 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0bdcb6KGonosfPZ6m.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-24/images/0eMboSxWLEJBS_EyL.webp b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0eMboSxWLEJBS_EyL.webp new file mode 100644 index 0000000000..dce6412427 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0eMboSxWLEJBS_EyL.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-24/images/0fWo9ieYzQcJjwQBi.webp b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0fWo9ieYzQcJjwQBi.webp new file mode 100644 index 0000000000..56cc49fb1f Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0fWo9ieYzQcJjwQBi.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-24/images/0gIu2Wm9mpuMGb1JY.webp b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0gIu2Wm9mpuMGb1JY.webp new file mode 100644 index 0000000000..529f6d0587 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0gIu2Wm9mpuMGb1JY.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-24/images/0tdpToIuTd-mBq-_R.webp b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0tdpToIuTd-mBq-_R.webp new file mode 100644 index 0000000000..8a9fcf8f12 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0tdpToIuTd-mBq-_R.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-24/images/0tt9hlqhTbrmj4ZCt.webp b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0tt9hlqhTbrmj4ZCt.webp new file mode 100644 index 0000000000..2af696be01 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0tt9hlqhTbrmj4ZCt.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-24/images/0uxeGJuIKWu0dPW1d.webp b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0uxeGJuIKWu0dPW1d.webp new file mode 100644 index 0000000000..a24de4e04a Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0uxeGJuIKWu0dPW1d.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-24/images/0wcmJCv-6owwsolRl.webp b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0wcmJCv-6owwsolRl.webp new file mode 100644 index 0000000000..198c60326b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-24/images/0wcmJCv-6owwsolRl.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-24/images/16ytqSvtR2TJzAE6LntHTGw.webp b/sites/www/content/blog/whats-new-in-flutter-3-24/images/16ytqSvtR2TJzAE6LntHTGw.webp new file mode 100644 index 0000000000..9d5b0a5db6 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-24/images/16ytqSvtR2TJzAE6LntHTGw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-24/images/1EA-cAY6uwkYqYb725vnCIw.webp b/sites/www/content/blog/whats-new-in-flutter-3-24/images/1EA-cAY6uwkYqYb725vnCIw.webp new file mode 100644 index 0000000000..86d2273c47 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-24/images/1EA-cAY6uwkYqYb725vnCIw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-24/index.md b/sites/www/content/blog/whats-new-in-flutter-3-24/index.md new file mode 100644 index 0000000000..4fdaf071ab --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-3-24/index.md @@ -0,0 +1,279 @@ +--- +title: "What’s new in Flutter 3.24" +description: "Unleashing Flutter GPU, Multi-View Embedding, and More" +publishDate: 2024-08-06 +author: itsjustkevin +image: images/0bdcb6KGonosfPZ6m.webp +category: releases +layout: blog +--- + +<DashImage figure src="images/1EA-cAY6uwkYqYb725vnCIw.webp" alt="Flutter 3.24 on a blue background" caption="Flutter 3.24 on a blue background" /> + + +Welcome to the latest Flutter update! Flutter 3.24 is packed with exciting new features and enhancements to elevate your app development experience. This release highlights the preview of Flutter GPU, which enables advanced graphics and 3D scenes directly in Flutter. Web apps can now embed multiple Flutter views, enhancing your app’s versatility. And finally, we’ve added video ad monetization support to help you maximize revenue. + +In the past few months, the Flutter community has been incredibly active, with 852 framework commits and 615 engine commits. We’re excited to welcome 49 new contributors who have helped make this release possible. Your dedication and passion are what drive Flutter forward. + +So, dive in and discover all the new features and enhancements that the Flutter community has brought to this latest release! + +## Flutter Framework + +### New Slivers + +This release adds new slivers that can be composed together for dynamic app bar behaviors: + +* [PinnedHeaderSliver](http://api.flutter.dev/flutter/widgets/PinnedHeaderSliver-class.html) + +* [SliverResizingHeader](http://api.flutter.dev/flutter/widgets/SliverResizingHeader-class.html) + +You can use these new slivers to make headers that float, stay pinned, or resize as the user scrolls. These new slivers are similar to the existing [`SliverPersistentHeader`](https://api.flutter.dev/flutter/widgets/SliverPersistentHeader-class.html) and [`SliverAppBar`](http://sliverappbar) slivers, but have simpler APIs that can be combined for greater effect. + +These new slivers come with new sample code. For example, `PinnedHeaderSliver`’s API documentation has an example that recreates effects like the iOS Settings app’s app bar: + +<DashImage figure src="images/0bdcb6KGonosfPZ6m.webp" /> + + +### Updates in the Cupertino library + +In this release, we’ve improved the fidelity of [`CupertinoActionSheet`](https://api.flutter.dev/flutter/cupertino/CupertinoActionSheet-class.html)**.** Sliding your finger across the action sheet’s buttons now provides haptic feedback. The buttons’ font size and weight now match their native counterparts. + +<DashImage figure src="images/0_o_9V0RlITOZdU4v.webp" /> + + +We’ve also added new focus properties to [`CupertinoButton`](https://api.flutter.dev/flutter/cupertino/CupertinoButton-class.html), and you can now customize the color of a disabled [`CupertinoTextField`](https://api.flutter.dev/flutter/cupertino/CupertinoTextField-class.html). + +A refresh of the Cupertino library is underway, expect more updates in future releases! + +### TreeView + +The [`two_dimensional_scrollables`](https://pub.dev/packages/two_dimensional_scrollables) package released the [`TreeView`](https://pub.dev/documentation/two_dimensional_scrollables/latest/two_dimensional_scrollables/TreeView-class.html) widget, alongside several companion classes for building performant scrolling trees that can scroll in all directions as the tree grows. The sample app included in the package was also updated with several new examples of using the [`TableView`](https://pub.dev/documentation/two_dimensional_scrollables/latest/two_dimensional_scrollables/TableView-class.html) and [`TreeView`](https://pub.dev/documentation/two_dimensional_scrollables/latest/two_dimensional_scrollables/TreeView-class.html) widgets. + +<DashImage figure src="images/0wcmJCv-6owwsolRl.webp" /> + + +[`TreeSliver`](https://api.flutter.dev/flutter/widgets/TreeSliver-class.html) was also added to the framework for building trees in one dimensional scrolling. The `TreeView` and `TreeSliver` APIs match, making it easy to switch between the one that is right for your use case. + +### CarouselView + +This release included the [Material Design carousel](https://m3.material.io/components/carousel/overview) widget: `CarouselView`. The `CarouselView` presents the “[Uncontained](https://m3.material.io/components/carousel/specs#477de3a1-c9df-4742-baf3-bcd5eeb3764c)” layout: a scrollable list of items that scroll to the edge of the container, and the leading and the trailing items can dynamically change size when they are scrolling out of the view and scrolling in the view. + +<DashImage figure src="images/16ytqSvtR2TJzAE6LntHTGw.webp" /> + + +### More functionality available from widgets + +This release contains some of the work to relocate core widget logic, that isn’t design specific, out of the Material library and into the Widgets library for more general use. This includes: + +* The [`Feedback`](https://github.com/flutter/flutter/pull/148523) widget, which provides easy access to haptic and audio feedback from the device in response to gestures like tap, long press, and more. + +* [`ToggleableStateMixin` and `ToggleablePainter`](https://github.com/flutter/flutter/pull/148272), base classes for building toggle widgets like checkboxes, switches, and radio buttons. + +### Enhanced enum features for AnimationStatus + +In a fantastic [contribution](https://github.com/flutter/flutter/pull/147801) from community member [@nate-thegrate](https://github.com/nate-thegrate), enhanced enum features were added to [AnimationStatus](https://api.flutter.dev/flutter/animation/AnimationStatus.html), including getters: + +* isDismissed + +* isCompleted + +* isRunning + +* isForwardOrCompleted + +Some of these getters already existed in [`Animation`](https://api.flutter.dev/flutter/animation/Animation-class.html) subclasses, like [`AnimationController`](https://api.flutter.dev/flutter/animation/AnimationController-class.html) and [`CurvedAnimation`](https://api.flutter.dev/flutter/animation/CurvedAnimation-class.html). Now all of these status getters are available in Animation subclasses in addition to AnimationStatus. Lastly, a [`toggle`](https://api.flutter.dev/flutter/animation/AnimationController/toggle.html) method was added to AnimationController to switch the direction of the animation. + +### Updates in SelectionArea + +Flutter’s [`SelectionArea`](https://api.flutter.dev/flutter/material/SelectionArea-class.html) now supports more native gestures associated with a triple click using a mouse, and double tapping on a touch device. By default, the [`SelectionArea`](https://api.flutter.dev/flutter/material/SelectionArea-class.html) and [`SelectableRegion`](https://api.flutter.dev/flutter/widgets/SelectableRegion-class.html) widgets use these new gestures. + +Triple click + +* Triple click + drag: Extends the selection in paragraph blocks. + +* Triple click: Selects the paragraph block at the clicked position. + +<DashImage figure src="images/0_GmbsIKfnwEAW6G8.gif" /> + + +Double tap + +* Double tap + drag: extends the selection in word blocks (supported on native Android/Fuchsia/iOS, and iOS web). + +* Double tap: selects the word at the tapped position (supported on native Android/Fuchsia/iOS, and Android/Fuchsia web). + +<DashImage figure src="images/0DwZgIybQr3FDFxXd.gif" /> + + +## Engine + +### Impeller + +**Improving Performance and Fidelity** + +In anticipation of removing the Impeller opt-out on iOS in a stable release coming soon, the team has been hard at work on improvements to Impeller’s performance and fidelity. As one example, a long series of improvements to text rendering [greatly increased the performance of emoji scrolling](https://github.com/flutter/flutter/issues/138798), eliminating jank when scrolling a large collection of emojis, which was a fantastic stress test of Impeller’s text rendering capabilities. + +Additionally, by [solving a number of issues](https://github.com/flutter/engine/pull/53042) we have also greatly improved the fidelity of Impeller’s text rendering in this release. In particular, text weight, spacing, and kerning now all match the fidelity of text with the legacy renderer. + +<DashImage figure src="images/0gIu2Wm9mpuMGb1JY.webp" alt="Before (Note gaps instead of proper kerning, and lighter than intended font weight.)" caption="Before (Note gaps instead of proper kerning, and lighter than intended font weight.)" /> + + +<DashImage figure src="images/0tt9hlqhTbrmj4ZCt.webp" alt="After" caption="After" /> + + +<DashImage figure src="images/0eMboSxWLEJBS_EyL.webp" alt="Before (Note incorrect spacing, for example in “vergelijken”)" caption="Before (Note incorrect spacing, for example in “vergelijken”)" /> + + +<DashImage figure src="images/0uxeGJuIKWu0dPW1d.webp" alt="After" caption="After" /> + + +**Android Preview** + +In this release, we are continuing to preview Impeller on Android. We have extended the preview period due to difficulties stemming from [a bug in Android 14](https://github.com/flutter/flutter/issues/146499#issuecomment-2082873125) that affects APIs that Impeller uses for Platform Views. The bug has since been patched by the Android team, however many deployed devices will be running the unpatched Android version for the foreseeable future. Working around these issues implies an additional API migration and therefore an additional stable release cycle. Out of an abundance of caution and to ensure that Flutter apps work on the widest possible range of devices, we are holding off on making Impeller the default renderer until a stable release later this year. + +As the Impeller preview on Android continues through the 3.24 stable cycle, we request that Flutter developers upgrade to the latest stable version, and file issues about any shortcomings noticed when Impeller is enabled. Feedback at this stage is invaluable to ensuring that Impeller is successful on Android and that we will be able to confidently make it the default renderer in a release later this year. The Android hardware ecosystem is much more diverse than the iOS ecosystem. For that reason, the most helpful feedback about Impeller should include detailed information about the specific device and Android version where issues occurred. + +### Improved defaults for downscaled images + +In this release, the default `FilterQuality` for images has been changed from `FilterQuality.low` to `FilterQuality.medium`. When a large image is substantially larger than its destination rectangle, which is a common scenario, `FilterQuality.low` causes an image to look more ‘pixelated’ and be slower to render than `FilterQuality.medium`. Going forward the team is also exploring more suitable names for the various `FilterQuality` levels. + +### Flutter GPU preview + +Flutter has introduced a major update for rendering capabilities with the Flutter GPU, available on the main channel. This low-level graphics API allows developers to create custom renderers using Dart code and GLSL shaders without needing any native platform code. + +Flutter GPU expands what you can render directly in Flutter, enabling advanced graphics and 3D scenes. It requires the Impeller rendering backend, currently supported on iOS, macOS, and Android. Although in early preview, Flutter GPU aims to eventually support all Flutter platforms. + +The API allows for full control over render pass attachments, the vertex stage, and data upload to the GPU. This flexibility is essential for creating sophisticated rendering solutions, from 2D character animations to complex 3D scenes. + +Developers can start using Flutter GPU by switching to the main channel and adding the flutter_gpu package to their projects. The upcoming months will see more functionality and stability improvements, with higher-level rendering libraries like flutter_scene simplifying the use of these advanced features. + +For a deeper dive into Flutter GPU and to see how you can leverage it in your projects, check out the detailed [Flutter GPU blog post](https://medium.com/flutter/getting-started-with-flutter-gpu-f33d497b7c11). Whether you’re creating games or complex graphics, Flutter’s new GPU capabilities make it a robust choice for your product. + +## Web + +## Multi-view embedding + +Flutter web applications can now leverage multi-view embedding, allowing developers to render content into multiple HTML elements simultaneously. This feature, known as “embedded mode” or “multi-view,” offers flexibility for integrating Flutter views into existing web applications. + +In multi-view mode, a Flutter web application doesn’t render immediately upon launch. Instead, it waits until the host application adds the first “view” using the addView method. The host application can dynamically add or remove these views, and Flutter adjusts its widgets accordingly. + +To enable multi-view mode, set `multiViewEnabled: true` in the `initializeEngine` method within the `flutter_bootstrap.js` file. Views can then be managed from JavaScript, adding them to specified HTML elements and removing them as needed. Each view addition and removal triggers updates within Flutter, allowing for dynamic content rendering. + +This feature is particularly useful for integrating Flutter into complex web applications where multiple, independent Flutter views are required. It also supports custom initialization data for each view, enabling personalized configurations and interactive experiences. + +For a deeper dive into implementing multi-view embedding in your Flutter web applications, check out the [detailed documentation](https://docs.flutter.dev/platform-integration/web/embedding-flutter-web). + +## Monetization + +## Video ad monetization support + +We have launched a new [Interactive Media Ads (IMA) plugin](https://pub.dev/packages/interactive_media_ads) to support instream video ad monetization on Flutter mobile apps. The new IMA plugin provides new ad monetization opportunities for Flutter apps on top of the existing [Google Mobile Ads (GMA) plugin](https://pub.dev/packages/google_mobile_ads) which primarily supports display ad formats. + +Instream video ads are typically shown to a user in the video player before (preroll), during (midroll), or after (postroll) video content playback. Some instream video ads can be skippable as well. + +<DashImage figure src="images/0_c0s8KMdLNyfxOMQ.webp" /> + + +**Flutter IMA benefits**: + +* Seamlessly monetize video player content on Flutter apps. For example, when an app user clicks **play** on video content, you can now implement the Flutter IMA plugin to first show the user a 15 second ad before starting the video content. + +* Tap into the same benefits of native IMA SDK including access to premium Google ad demand and industry standard compliance (such as [IAB VAST](https://iabtechlab.com/standards/vast/)). + +The initial launch version currently supports pre-roll video ads on Android and iOS platform. Support for midroll ads will be available soon. We encourage you to start exploring the new IMA plugin on your Flutter app video content. Please let us know if have any issues or concerns on [GitHub](https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+interactive_media_ads%22). + +Resources: [Plugin guide](https://pub.dev/packages/interactive_media_ads), [Sample App](https://pub.dev/packages/interactive_media_ads/example), [Git Repository](https://github.com/flutter/packages/tree/main/packages/interactive_media_ads) + +## iOS + +## Swift Package Manager initial support + +Today, Flutter uses CocoaPods to manage native iOS or macOS dependencies. + +Flutter 3.24 adds early support for Swift Package Manager. This brings several benefits, including: + +1. **Access to the Swift package ecosystem**. Flutter plugins will be able to leverage the growing ecosystem of [Swift packages](https://swiftpackageindex.com/)! +2. **Simplifies Flutter installation**. Swift Package Manager is bundled with Xcode. In the future, you won’t need to install Ruby and CocoaPods to use Flutter for Apple’s platforms. + +We encourage plugin authors to[ try adding support for Swift Package Manager to your plugins](https://docs.flutter.dev/packages-and-plugins/swift-package-manager/for-plugin-authors#how-to-add-swift-package-manager-support-to-an-existing-flutter-plugin), and provide feedback on your experience. + +If you have feedback on Flutter’s support for Swift Package Manager, please [file an issue](https://github.com/flutter/flutter/issues/new/choose). + +## Ecosystem + +## Shared Preferences plugin updates + +We’ve added two new APIs to the [shared_preferences](https://pub.dev/packages/shared_preferences) plugin, SharedPreferencesAsync and SharedPreferencesWithCache. The most significant change is that the Android implementation uses Preferences DataStore instead of Shared Preferences. + +SharedPreferencesAsync allows users to call directly to the platform to get the most up to date preferences saved on the device, at the cost of being async and a little slower than using the cached version. This is useful for preferences that can be updated by other systems or isolates, which would make a cache stale. + +SharedPreferencesWithCache is built on top of SharedPreferencesAsync and allows users to access a locally cached copy of the preferences synchronously. This is similar to the old API, but can now be instantiated multiple times with different parameters. + +These new APIs are intended to replace the current SharedPreferences API in the future. However, this is one of the most used plugins in the ecosystem, and we know it will take time for the ecosystem to switch to the new APIs. + +## Flutter and Dart Package Ecosystem Summit Europe 2024 + +<DashImage figure src="images/0fWo9ieYzQcJjwQBi.webp" /> + + +As part of Fluttercon Europe 2024, we held the first in-person Flutter and Dart Package Ecosystem Summit. This followed on from the first virtual summit we held in August 2023. See a summary of takeaways from the discussion sessions [here](https://docs.google.com/document/d/e/2PACX-1vRFLdpIJYO5YPARcyUT1FYPzwkFb1hxh_agqnCXxsyirXocLZS5jobs3xFV5ZGpSQHLHZiBzqbJlXNV/pub). + +We’re excited to announce that the next summit will be at [Fluttercon USA](https://flutterconusa.dev/) in New York City on 20th September 2024! If you’re a package author or contributor, and are attending Fluttercon USA 2024, [register](https://rsvp.withgoogle.com/events/flutter-package-ecosystem-summit-usa-2024) to save your spot in the summit. + +The summit brought together package authors and maintainers for unconference-style sessions on the following topics: + +* Session 1 — Past, Present and Future of Native Interop + +* Session 2 — Sustainable package maintenance models + +* Session 3 — Addressing package ecosystem fragmentation + +We believe the summit, especially when part of more general Flutter and Dart events, is a valuable platform for open discussion among the community, to surface critical challenges and brainstorming solutions. We’re looking forward to holding more of these summits going forward, in partnership with the community. + +## DevTools and IDEs + +This release includes some exciting improvements to the Flutter DevTools tooling suite. + +If you have ever wondered whether your Flutter app is building more Widgets than expected, a new feature in the **DevTools Performance** tool that can help with that. Using the new **Rebuild Stats** feature, you can capture information about how many times a widget was built in your app or even in a specific Flutter frame. + +<DashImage figure src="images/07LXjXlFR9w-VR6xk.webp" /> + + +*A screenshot of the DevTools Performance tool tracking rebuild stats.* + +We’ve added polish and critical bug fixes to tools like the **Network profiler** and the **Flutter Deep Links tool**, and have made some general improvements to provide you with a better experience when you are using DevTools from within your IDE. Speaking of IDEs, did you know that you can use every DevTools tool directly inside your IDE? + +<DashImage figure src="images/0tdpToIuTd-mBq-_R.webp" /> + + +*DevTools screens open inside a VS Code window.* + +<DashImage figure src="images/0QvoXCNqqfsrMn1oC.webp" /> + + +*DevTools screens open inside Android Studio tool windows.* + +This release includes improvements to the Flutter Sidebar in VS Code to give you easier access to the tools you are looking for. Upgrade to the latest versions of VS Code and the Flutter and Dart extensions to access the improved sidebar. + +<DashImage figure src="images/0BzEybhMlV-SnSZZl.webp" /> + + +*The Flutter Sidebar is adaptive and scales to fit your workspace* + +This release also includes some major improvements to the [DevTools Extensions](https://docs.flutter.dev/tools/devtools/extensions) framework. You can now use DevTools extensions (tools provided by one of your package dependencies) when you are debugging a Dart or Flutter test, or even when you aren’t debugging anything, but are just writing code in your IDE. So, if you’ve wanted to use a tool (or build one!) for one of these user journeys, it is now possible. + +To learn more about all the updates included in Flutter 3.24 check out the release notes for DevTools [2.35.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.35.0), [2.36.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.36.0), and [2.37.2](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.37.2). + +## Breaking changes and deprecations + +Breaking changes in this release include changes to [Navigator’s pages API](https://docs.flutter.dev/release/breaking-changes/navigator-and-page-api), [generic types in PopScope](https://docs.flutter.dev/release/breaking-changes/popscope-with-result), [Flutter web’s default renderer](https://docs.flutter.dev/platform-integration/web/renderers) and the introduction of some new deprecations. See the full list of migration guidance on the [breaking changes page](https://docs.flutter.dev/release/breaking-changes). + +As always, many thanks to the community for [contributing tests](https://github.com/flutter/tests/blob/master/README.md) — these help us identify these breaking changes. To learn more, check out [Flutter’s breaking change policy](https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes). + +## Conclusion + +At the heart of Flutter’s success is you — our amazing community. This release wouldn’t be possible without your countless contributions and unwavering passion. From the bottom of our hearts, thank you. + +To dive into the specifics of what has been achieved with this release, we invite you to view the [release notes](https://docs.flutter.dev/release/release-notes/release-notes-3.24.0) and [change log](https://github.com/flutter/flutter/blob/main/CHANGELOG.md) for a comprehensive list of additions in Flutter 3.24. + +Flutter 3.24, alongside Dart 3.5, is now available on the stable channel. Embarking on this latest journey with Flutter is as straightforward as running flutter upgrade. We can’t wait to see what you build! \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/0CNcQF3yqFwc0IGNc.gif b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0CNcQF3yqFwc0IGNc.gif new file mode 100644 index 0000000000..9c83330f40 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0CNcQF3yqFwc0IGNc.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/0DdHEiQ7FUmFVIMGz.webp b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0DdHEiQ7FUmFVIMGz.webp new file mode 100644 index 0000000000..fca9792790 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0DdHEiQ7FUmFVIMGz.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/0ECVkEydB6YuBSGs_.webp b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0ECVkEydB6YuBSGs_.webp new file mode 100644 index 0000000000..055407521e Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0ECVkEydB6YuBSGs_.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/0EUjz8K6BQZX2uumi.webp b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0EUjz8K6BQZX2uumi.webp new file mode 100644 index 0000000000..748b31e8e8 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0EUjz8K6BQZX2uumi.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/0J2ur0Z_wJsdbAUt8.webp b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0J2ur0Z_wJsdbAUt8.webp new file mode 100644 index 0000000000..8e3003defe Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0J2ur0Z_wJsdbAUt8.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/0JYBUuzXHcYTCD4i5.webp b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0JYBUuzXHcYTCD4i5.webp new file mode 100644 index 0000000000..9b72ae41a0 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0JYBUuzXHcYTCD4i5.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/0Nr0WuNhNxjlI1Pqg.webp b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0Nr0WuNhNxjlI1Pqg.webp new file mode 100644 index 0000000000..33870dfe38 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0Nr0WuNhNxjlI1Pqg.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/0QwWCiz-DAHklZZAi.gif b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0QwWCiz-DAHklZZAi.gif new file mode 100644 index 0000000000..2b7a1a44ee Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0QwWCiz-DAHklZZAi.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/0WO_tOgbNKHtMAJ4h.webp b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0WO_tOgbNKHtMAJ4h.webp new file mode 100644 index 0000000000..6db66832ca Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0WO_tOgbNKHtMAJ4h.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/0XUtBHK9h0zbWrKUF.webp b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0XUtBHK9h0zbWrKUF.webp new file mode 100644 index 0000000000..a357e6a45e Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0XUtBHK9h0zbWrKUF.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/0ZPrThBB6ZN8DEtUq.webp b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0ZPrThBB6ZN8DEtUq.webp new file mode 100644 index 0000000000..07c44f3ebb Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0ZPrThBB6ZN8DEtUq.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/0_uQf9sIr4M1EC34y.webp b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0_uQf9sIr4M1EC34y.webp new file mode 100644 index 0000000000..f849d1f685 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0_uQf9sIr4M1EC34y.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/0dngArPlUvp4pVA-R.webp b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0dngArPlUvp4pVA-R.webp new file mode 100644 index 0000000000..ac165dccaf Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0dngArPlUvp4pVA-R.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/0tIfxBdBCHFzYhStI.webp b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0tIfxBdBCHFzYhStI.webp new file mode 100644 index 0000000000..e1b8521157 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0tIfxBdBCHFzYhStI.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/0vdSn5c7uavWstNO5.webp b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0vdSn5c7uavWstNO5.webp new file mode 100644 index 0000000000..0ebecb42ee Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0vdSn5c7uavWstNO5.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/0vyFYrAWVig8f8WGl.webp b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0vyFYrAWVig8f8WGl.webp new file mode 100644 index 0000000000..181d27d206 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0vyFYrAWVig8f8WGl.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/0wsAPbKV2yW8tLXNb.webp b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0wsAPbKV2yW8tLXNb.webp new file mode 100644 index 0000000000..290dbb3690 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/0wsAPbKV2yW8tLXNb.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/185CZePbMbJ99B5-hPvwpKQ.webp b/sites/www/content/blog/whats-new-in-flutter-3-27/images/185CZePbMbJ99B5-hPvwpKQ.webp new file mode 100644 index 0000000000..36b229c36f Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/185CZePbMbJ99B5-hPvwpKQ.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/images/1auh3JCfdFKzK_licnLrEKQ.webp b/sites/www/content/blog/whats-new-in-flutter-3-27/images/1auh3JCfdFKzK_licnLrEKQ.webp new file mode 100644 index 0000000000..efe12d5078 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-27/images/1auh3JCfdFKzK_licnLrEKQ.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-27/index.md b/sites/www/content/blog/whats-new-in-flutter-3-27/index.md new file mode 100644 index 0000000000..640fbe7fbf --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-3-27/index.md @@ -0,0 +1,380 @@ +--- +title: "What’s new in Flutter 3.27" +description: "Elevating App Development with Impeller, Cupertino Updates, and More" +publishDate: 2024-12-11 +author: itsjustkevin +image: images/1auh3JCfdFKzK_licnLrEKQ.webp +category: releases +layout: blog +--- + +<DashImage figure src="images/1auh3JCfdFKzK_licnLrEKQ.webp" /> + + +## What’s new in Flutter 3.27 + +### Elevating App Development with Impeller, Cupertino Updates, and More + +## Introduction + +Let’s dive into Flutter 3.27! This release is brimming with enhancements and features focused on refining your development experience and boosting your app’s performance. + +We’ve got updates across the framework, engine, and ecosystem, including progress with Impeller and improvements to Cupertino widgets. Plus, we’re introducing exciting new features in DevTools, making it easier than ever to manage your Flutter projects. With over 1,400 commits from 187 contributors (including **49 first time contributors**!), Flutter 3.27 is a testament to the incredible energy and collaboration of the Flutter community. Let’s explore what’s new! + +## Framework + +### Cupertino updates + +This release includes high fidelity updates to `CupertinoCheckbox` and `CupertinoRadio`, featuring updates to sizes, colors, stroke widths, and behaviors when pressed. There have also been large customizability updates to `CupertinoRadio`, `CupertinoCheckbox`, and `CupertinoSwitch`, adding properties like mouse cursors, semantic labels, thumb images, and fill colors. Some properties, like the inactive color of `CupertinoCheckbox`, were deprecated, while others, like the track color of `CupertinoSwitch`, were renamed. These changes bring these widgets up to feature parity with their Material counterparts. + +`CupertinoSlidingSegmentedControl` sees some fidelity updates to its thumb radius, separator height, padding, shadows, and thumb scale alignment. It also now supports disabling individual segments, and proportional layout based on segment content. + +<DashImage figure src="images/0Nr0WuNhNxjlI1Pqg.webp" /> + + +Thanks to an amazing contribution from community member [@veloce](https://github.com/veloce), both `CupertinoNavigationBar`’s and `CupertinoSliverNavigationBar`’s backgrounds are now transparent until content is scrolled under them. + +<DashImage figure src="images/0dngArPlUvp4pVA-R.webp" /> + + +This allows the sliver navigation bar to have the same color as its background in its expanded state, but a different customizable color in its collapsed state (and the ability to lerp between both colors on scroll). + +Thanks to an amazing contribution from community member [@kerberjg](http://github.com/kerberjg), you can now apply the iOS 15+ button styles using the new `CupertinoButtonSize` enum with the new `sizeStyle` property in `CupertinoButton`. Use the new `CupertinoButton.tinted` constructor to make buttons with a translucent background. + +<DashImage figure src="images/0vdSn5c7uavWstNO5.webp" /> + + +<DashImage figure src="images/0ZPrThBB6ZN8DEtUq.webp" /> + + +`CupertinoButton` also has a new `onLongPress` handler, and allows performing actions through keyboard shortcuts. + +`CupertinoPicker` and `CupertinoDatePicker` will now scroll to tapped items. + +<DashImage figure src="images/0XUtBHK9h0zbWrKUF.webp" /> + + +`CupertinoAlertDialog` now supports the tap-slide gesture. + +<DashImage figure src="images/0JYBUuzXHcYTCD4i5.webp" /> + + +`CupertinoActionSheet` sees some high fidelity updates, including adjusting padding and font sizes across all system text size settings and supporting haptic feedback when sliding over buttons. + +Other changes include `CupertinoContextMenu` now supporting scrolling if its actions overflow the screen, `CupertinoDatePicker` no longer clipping long contents in its columns, and `CupertinoMagnifier` supporting zoom effects by providing a magnification scale. + +### Normalizing Material theming + +In this release, `CardTheme`, `DialogTheme` and `TabBarTheme` have been refactored to make them 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. Future releases will continue to normalize component themes like these for a more consistent theming experience in the material library. + +### More Features for CarouselView + +This release introduces `CarouselView.weighted`, enabling more dynamic layouts within carousels. By adjusting the `flexWeights` parameter in the constructor, you can achieve a variety of item layouts. For instance, [3, 2, 1] creates a [multi-browse](https://m3.material.io/components/carousel/specs#3c9dc903-2f88-4b27-84e3-213c50674632) layout, [7, 1] produces a [hero](https://m3.material.io/components/carousel/specs#66eb8746-70f0-4bad-b940-8e1028268d65) layout, and [1, 7, 1] yields a [centered-hero](https://m3.material.io/components/carousel/specs#92c779ce-de8b-4dee-8201-95d3e429204f) layout. These values represent the relative weight each item occupies within the carousel view and can be customized to suit users’ specific needs. + +<DashImage figure src="images/0EUjz8K6BQZX2uumi.webp" /> + + +### More in Material + +This release includes a number of small feature enhancements and bug fixes. Some enhancement examples: + +* `SegmentedButton` now has a `direction` property, so items can be aligned vertically. + +<DashImage figure src="images/0ECVkEydB6YuBSGs_.webp" /> + + +* more icon-related properties are added to the styleFrom method for `ButtonStyleButton` classes (`ElevatedButton`, `FilledButton`, `OutlinedButton`, `TextButton`, `MenuItemButton`, and `SubmenuButton`) to allow more customization. + +Some bug fixes examples: + +* The Icon size and color defaults for `ButtonStyleButton` classes are aligned with Material 3 specifications. + +* The AppBar’s scroll-under behavior now correctly persists when the navigation drawer opens, matching the native Android experience. + +* `MenuAnchor` is further improved with focus fixes and several `DropdownMenu` issues, including the scrolling issue within nested scrollables and filter mechanism behavior, have been resolved. + +### Mixing Route Transitions + +[ModalRoutes](https://api.flutter.dev/flutter/widgets/ModalRoute-class.html) are now more flexible when the previous and new routes have different page transitions. When a new route enters the screen, sometimes they need the previous existing route to play a certain transition to sync up with the entrance transition of the new route. Now `ModalRoutes` can provide exit transition builders to each other so the enter and exit transitions always sync up. This allows pages to have multiple route transition options on one page using Flutter’s Navigator and Router. + +<DashImage figure src="images/0vyFYrAWVig8f8WGl.webp" /> + + +### Text Selection Improvements + +Flutter’s `SelectionArea` now supports Shift + Click gesture to move the extent of the selection to the clicked position on Linux, macOS, and Windows. + +<DashImage figure src="images/0QwWCiz-DAHklZZAi.gif" /> + + +You can also now clear the selection under a SelectionArea and `SelectableRegion` through the method [`clearSelection`](https://main-api.flutter.dev/flutter/widgets/SelectableRegionState/clearSelection.html) on [`SelectableRegionState`](https://api.flutter.dev/flutter/widgets/SelectableRegionState-class.html). [`SelectableRegionState`](https://api.flutter.dev/flutter/widgets/SelectableRegionState-class.html) is also now accessible through `SelectionArea` by providing it a `GlobalKey` and accessing its [`SelectionAreaState.selectableRegion`](https://main-api.flutter.dev/flutter/material/SelectionAreaState/selectableRegion.html). + +We also resolved some issues with RenderParagraph, so selecting text under a `SelectionArea` or `SelectableRegion` still works as expected after resizing the [window](https://github.com/flutter/flutter/pull/155719), and when clicking or [tapping outside](https://github.com/flutter/flutter/pull/155892) of the actual text. + +### Row and Column spacing + +This release also features a new convenience to using Rows and Columns, spacing! This double parameter will apply a fixed amount of pixels to offset children of Rows and Columns from each other, reducing the previous amount of code required to do so. + +<DashImage figure src="images/185CZePbMbJ99B5-hPvwpKQ.webp" /> + + +## Engine + +### Impeller on Android + +We are pleased to share that Impeller is now the default rendering engine on modern Android devices. While Impeller on these devices has been in preview since Flutter’s 3.16 stable release in November 2023, since then we have made large improvements to both performance and fidelity based on user feedback. + +On older Android devices and devices without Vulkan support, the Skia rendering engine will still be used as before. While we believe that Impeller’s performance and fidelity are both high quality, users are able to opt-out and return to the legacy Skia renderer by passing`--no-enable-impeller` to the `flutter` command line tool, or by placing the following in your `AndroidManifest.xml` file(s): + +`<meta-data + android:name=”io.flutter.embedding.android.EnableImpeller” + android:value=”false” />` + +Going forward we will continue to make improvements to Impeller’s performance and fidelity on Android. Additionally, we intend to make Impeller’s OpenGL backend production ready to remove the Skia fallback. + +As during the preview period, we request that Flutter developers upgrade to the latest stable version, and file issues about any shortcomings noticed when Impeller is enabled. Feedback continues to be invaluable to ensuring that Impeller is successful on Android. The Android hardware ecosystem is much more diverse than the iOS ecosystem. For that reason, the most helpful feedback about Impeller should include detailed information about the specific device and Android version where issues occurred. + +## Improvements on iOS devices + +In previous versions of Flutter, users may have experienced [an issue](https://github.com/flutter/flutter/issues/134959) in which applications on iOS devices waited several milliseconds per frame on compositor backpressure. This backpressure would be observed as delays in the beginning of the raster workload. In many circumstances, this delay caused missed frames and jank. This effect was especially noticeable on high frame-rate devices with a smaller frame time budget. + +In this release, open source contributor [@knopp](https://github.com/knopp) added [a new implementation](https://github.com/flutter/engine/pull/48226) of a Metal rendering surface that allows raster workloads to begin more consistently after the UI workload completes. This means that users will observe that overall frame times are more stable as there is less waiting on the iOS system compositor. In particular, Flutter will now much more consistently hit 120Hz on high frame-rate devices. In our benchmarks we have observed this improvement in a large reduction in average frame rasterization time across many scenarios. + +<DashImage figure src="images/0wsAPbKV2yW8tLXNb.webp" alt="*This chart shows an across-the-board decrease in average frame rasterization time in milliseconds on a large number of different benchmarks running on iPhone 11 devices.*" caption="*This chart shows an across-the-board decrease in average frame rasterization time in milliseconds on a large number of different benchmarks running on iPhone 11 devices.*" /> + + +### Framework Wide Gamut Colors + +Flutter now supports defining UI’s with colors in the DisplayP3 color space to take full advantage of displays on iOS devices. It also has wider bit depth to support more colors in sRGB and DisplayP3 color spaces. This change has led to the deprecation of some `Color` methods; more information can be found in the [breaking change notice](https://docs.flutter.dev/release/breaking-changes/wide-gamut-framework). + +## Web + +### Web Improvements + +This release brings a variety of improvements to Flutter web, focusing on performance, efficiency, and accessibility: + +* Image decoding in Safari and Firefox is now done using an `<img>` element instead of WebAssembly codecs for all static images. This removes jank related to image decoding and reduces WASM memory usage. + +* Platform views were optimized to reduce the number of canvas overlays, improving rendering efficiency. + +* All plugins and packages developed by the Flutter team are now compatible with WebAssembly. + +* Multiple accessibility fixes were implemented for headings, dialogs, passwords, iOS keyboard, links, and scrollables. + +* A number of rendering bugs were fixed in the CanvasKit and Skwasm renderers, including image filters, clipping, and memory usage of `Paint` objects. + +* Improved drag scrolling in multi-view mode. + +## iOS + +### Swift Package Manager + +Flutter is migrating to Swift Package Manager. This brings several benefits: + +1. **Access to the Swift package ecosystem**. Flutter plugins will be able to leverage the growing ecosystem of [Swift packages](https://swiftpackageindex.com/)! +2. **Simpler Flutter installation**. Swift Package Manager is bundled with Xcode. In the future, you won’t need to install Ruby and CocoaPods to use Flutter for Apple’s platforms. + +Previously, Swift Package Manager support was only available on Flutter’s “main” channel. We’re pleased to announce that the Swift Package Manager feature is now available on the “beta” and “stable” channels too! For now, the Swift Package Manager feature is off by default while we iron out the kinks. + +Plugin authors are encouraged to add support for [Swift Package Manager to your plugins](https://docs.flutter.dev/packages-and-plugins/swift-package-manager/for-plugin-authors#how-to-add-swift-package-manager-support-to-an-existing-flutter-plugin). Many popular plugins — including [Firebase](https://github.com/firebase/flutterfire/issues/13205), [plus plugins](https://github.com/fluttercommunity/plus_plugins/issues/3152), and [more](https://pub.dev/packages?q=is%3Aswiftpm-plugin) — have already migrated to Swift Package Manager! + +Pub.dev now checks plugins for Swift Package Manager compatibility. In the future, packages that are incompatible will not receive full package scores. + +If you have feedback on Flutter’s support for Swift Package Manager, please [file an issue](https://github.com/flutter/flutter/issues/new/choose). + +## Android + +### Edge to Edge + +By default on devices running Android 15+, your app will use edge-to-edge mode and run in full screen! For details on the change and how you can opt out of this behavior, see [the guide](https://docs.flutter.dev/release/breaking-changes/default-systemuimode-edge-to-edge). + +<DashImage figure src="images/0CNcQF3yqFwc0IGNc.gif" /> + + +### Support for Freeform + +[Android Freeform](https://android-developers.googleblog.com/2024/09/developer-preview-desktop-windowing-on-android-tablets.html) allows users to resize app windows and has been available as a developer option. Flutter’s `SafeArea` and `MediaQuery` have been updated to handle hardware cutouts when a freeform window moves into a hardware cutout. + +### Kotlin build script support and declarative plugin migration + +Developers can now use `build.gradle.kts` files. +Flutter tooling now supports Kotlin build files. If you encounter a place where we don’t support Kotlin, [please file a bug](https://github.com/flutter/flutter/issues/new/choose). Groovy remains a supported Gradle language. + +Flutter 3.27 is the last release that will support [Gradle’s legacy apply script method](https://docs.gradle.org/8.5/userguide/plugins.html#sec:script_plugins). See[ migration docs](https://docs.flutter.dev/release/breaking-changes/flutter-gradle-plugin-apply). + +**Note: Most apps will need to manually migrate. Newly created apps will[ use kotlin build files](https://github.com/flutter/flutter/pull/157195).** + +Moving to declarative inclusion of the Flutter Gradle Plugin helps Flutter migrate to Kotlin builds files with better type safety and easier to maintain unit tests for build code. It is also a part of a broader effort to bring more stability across Android tooling versions. + +## Ecosystem + +### Flutter and Dart Package Ecosystem Summit USA 2024 + +As part of Fluttercon USA 2024 in September, we held a Flutter and Dart Package Ecosystem Summit. This was our second in-person summit, that followed on from the summit at Fluttercon Europe in July 2024, and the first virtual summit we held in August 2023. See a summary of takeaways from the discussion sessions [here](https://docs.google.com/document/d/e/2PACX-1vRjPlBNo13I9P5UXpehKagVq8e8n7ppNz169HbKSgK4iTmCrbk5rzh-RN6ULpVJ3AkPnnCSI_VCMdYW/pub). + +The summit brought together package authors and maintainers for unconference-style sessions on the following topics: + +* Session 1 — Adding support for Swift Package Manager + +* Session 2 — Past, Present and Future of Native Interop + +* Session 3 — Sustainable package maintenance models + +We’re looking forward to holding more of these summits going forward, in partnership with the community. + +### Package download counts on pub.dev + +We launched download count metrics on pub.dev! + +Providing more precise popularity metrics on[ pub.dev](https://pub.dev) has been[ the most requested](https://github.com/dart-lang/pub-dev/issues/2714) feature in the site’s issue tracker for a long time. + +Now, when you view a package page you will see a 30-day download count replacing the former “popularity score”. + +Additionally, we added an interactive sparkline chart showing weekly download activity over time. This chart helps developers and package authors uncover trends in package usage. For example, it could be an indicator that a new release has caused a spike in usage, or that a package is gaining or losing popularity. + +A download is registered each time the package is downloaded from pub.dev. For example, when you run `flutter pub get`, and the package isn’t already in your pub cache. + +The download count numbers presented are raw download counts, and reflect usage from CI systems, package analysis, human pub client users, and more. + +<DashImage figure src="images/0tIfxBdBCHFzYhStI.webp" /> + + +### Pub workspaces + +We are launching [Pub Workspaces](https://dart.dev/go/pub-workspaces) with Dart 3.6, to support the development of multiple related packages in one monorepo + +By defining a root pubspec that references the other packages in the repository, running pub get anywhere in the repository will result in a shared resolution for all the packages. This ensures that all packages are developed with a consistent set of dependencies. + +The shared resolution is also picked up by the analyzer, which now only has to keep track of a single analysis context for the entire workspace, potentially resulting in a significant reduction in memory use when opening the whole repository in your IDE. + +### Automated publishing of Flutter packages from GitHub + +We have extended the setup-dart [publish](https://github.com/dart-lang/setup-dart/blob/main/.github/workflows/publish.yml) Github Actions workflow to also allow [automated publishing](https://dart.dev/tools/pub/automated-publishing#configuring-automated-publishing-from-github-actions-on-pub-dev) of Flutter packages to pub.dev, making it easier to contribute to the Pub ecosystem in a verifiable manner. + +### Launch of Interactive Media Ads plugin + +We launched an [Interactive Media Ads (IMA) Flutter plugin](https://pub.dev/packages/interactive_media_ads) that makes it easy to integrate multimedia ads into iOS and Android apps built with Flutter. The IMA plugin can request ads from any VAST-compliant ad server and manage ad playback in your apps. Ads play in a separate video player positioned on top of the app’s content video player. The current supported features are: + +* Client-side APIs for linear instream video ad requests + +* VAST & VMAP + +* Single & Podded Requests + +* Skippable Ads + +### In-App Purchase iOS and macOS plugin updates + +We’ve added [StoreKit 2](https://developer.apple.com/storekit/) support to the `in_app_purchase_storekit` package to migrate off StoreKit 1 APIs, which were deprecated in iOS 18. This allows us to add new StoreKit 2 features like better subscription management in the future. Stay tuned for an upcoming migration guide to help you transition to the newer StoreKit 2 functionality. This is a non-breaking change, so you can migrate whenever you wish. + +## DevTools and IDEs + +### Flutter DevTools updates + +This release includes new features, overall improvements to robustness and polish, as well as some exciting new experimental features that you can enable from DevTools. See the [Previews and Experimental features](https://docs.google.com/document/d/1dNTgjAR5CAmjK_aYgNPCBOnBatg164fws6qHkLGvBTA/edit?pli=1&tab=t.0#bookmark=id.5x1g42w1xm31) section below for more information on these. + +Firstly, we added support for validating iOS deep link settings in the Flutter Deep Links tool. You can now validate deep links for Android and iOS. + +<DashImage figure src="images/0WO_tOgbNKHtMAJ4h.webp" alt="*iOS deep link validation in the DevTools Deep Links tool.*" caption="*iOS deep link validation in the DevTools Deep Links tool.*" /> + + +Next, we’ve made some improvements to the workflow of working with “offline” data. Sometimes it is necessary to export the data you are viewing in DevTools for future use or for loading into another tool. This release adds support for exporting Network data as a `.har` file, and for loading memory snapshots into DevTools for viewing when DevTools is not connected to a running app. + +Additionally, if you’ve ever been in the middle of debugging a memory issue with DevTools and lost your memory tool data because your app crashed (perhaps due to the very OOM issue you are trying to debug), we feel your frustration! We have fixed this UX pain point by allowing you to continue viewing the most recent memory tool data in DevTools, even after an app disconnects. + +These are just a few highlights from this release. To learn more about all the updates included in Flutter 3.27, check out the release notes for DevTools [2.38.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.38.0), [2.39.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.39.0), and [2.40.1](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.40.1). + +## Previews and experimental features to try + +### Flutter DevTools — New Flutter Inspector + +The Flutter Inspector has undergone some major changes to improve usability and enhance your UI debugging journey. Enable the new Inspector by toggling the “New Inspector” setting. + +<DashImage figure src="images/0DdHEiQ7FUmFVIMGz.webp" alt="*New Flutter Inspector in DevTools.*" caption="*New Flutter Inspector in DevTools.*" /> + + +A few of the changes include: + +* A condensed Widget tree that makes it easier to view deeply nested Flutter widget trees. This is especially helpful when using the Flutter Inspector from inside your IDE, where horizontal screen space is limited. + +* An option to toggle whether implementation widgets should be included in the widget tree. Implementation widgets are widgets that you didn’t include in your app code, but rather that were added to the widget tree by the Flutter framework or other packages. + +* A details view for the selected widget that shows an inline layout viewer, widget and render object properties, and a flex layout explorer for Flex widgets and their children. + +More improvements are coming soon for the new Inspector, but these changes are ready for you to try out! Please file any[ issues](https://github.com/flutter/devtools/issues) you encounter so that we can fix them before enabling the new Inspector by default. + +### Flutter DevTools — Try with WebAssembly + +Enable the WebAssembly feature in DevTools settings to load the WASM-compiled DevTools web app. + +This should yield better performance than the default JS-compiled version of DevTools. This feature is experimental, so please file any[ issues](https://github.com/flutter/devtools/issues) you encounter with the WASM build. + +<DashImage figure src="images/0J2ur0Z_wJsdbAUt8.webp" alt="*DevTools setting to use the WebAssembly build instead of the Javascript build.*" caption="*DevTools setting to use the WebAssembly build instead of the Javascript build.*" /> + + +## Breaking Changes and Deprecations + +### Material Design 3 tokens + +The latest Material Design 3 tokens(v6.1) have been applied to the Flutter Material library. The Material Design tokens updated the mapping of 4 color roles in Light mode only to be more visually appealing while retaining accessible contrast. We did not identify any breakages in our testing of this change, which applied to these color properties: + +* On-primary-container (Primary10 to Primary30) + +* On-secondary-container (Secondary10 to Secondary30) + +* On-tertiary-container (Tertiary10 to Tertiary30) + +* On-error-container (Error10 to Error30) + +<DashImage figure src="images/0_uQf9sIr4M1EC34y.webp" /> + + +This affects widgets that use these roles as their default value. + +The border color of `Chip`s (`Chip`, `ActionChip`, `ChoiceChip`, `FilterChip`, and `InputChip`) was updated from `ColorScheme.outline` to `ColorScheme.outlineVariant`. This improves the visual hierarchy between chips and buttons. + +### Objective-C iOS projects + +Since Flutter 1.9.1 released in 2019, new iOS projects use Swift by default. Creating a[ new Objective-C iOS project is now deprecated](https://github.com/flutter/flutter/issues/148586), and the `flutter create --ios-language objc` flag will be removed in a future version of Flutter. You will still be able to open your Xcode project and add Objective-C files, including for plugins. Java continues to be supported for Android apps with the `flutter create --android-language java` flag. + +### Deep link default flag + +The default value for Flutter’s deep linking flag has changed from **false** to **true**, meaning deep linking is now opt-in by default. If you’re using Flutter’s default deep linking setup, there’s no need to manually set the flag to **true** anymore. + +However, if you’re using third-party plugins for deep links, such as: + +* [firebase dynamic links](https://firebase.google.com/docs/dynamic-links) + +* [uni_link](https://pub.dev/packages/uni_links) + +* [app_links](https://pub.dev/packages/app_links) + +This update will introduce a breaking change. In this case, you’ll need to manually reset the Flutter deep linking flag to **false**. + +For more details, refer to the design document: [flutter.dev/go/deep-link-flag-migration](http://flutter.dev/go/deep-link-flag-migration). + +### Deprecating support for older SDKs in IDE + +To ensure we can continue to deliver a high-quality developer experience, we’re making a change to our IDE plugin support. As the Dart and Flutter plugins for VS Code and IntelliJ have evolved, supporting older Dart SDK versions has become increasingly challenging. + +Starting with the release of Dart 3.6, we will deprecate support for Dart SDK versions older than 3.0 (released in May 2023). This means that while the tools may still function with older SDKs, we will no longer provide official support or fixes for issues specific to these versions. + +With the release of Dart 3.7 (estimated in Q1 2025), we will remove support for these older SDK versions entirely. Future releases of the plugins may not be compatible with these versions. + +**What does this mean for you?** + +* If you’re using an older Dart SDK, we encourage you to upgrade to version 3.0 or later to continue receiving support and updates. + +* This change affects less than 1% of our users, and it will allow us to focus on delivering the best possible experience for the vast majority of developers. + +### Closing + +For all breaking changes in this release, see the full list of migration guidance on the [breaking changes page](https://docs.flutter.dev/release/breaking-changes). + +As always, many thanks to the community for [contributing tests](https://github.com/flutter/tests/blob/master/README.md) — these help us identify breaking changes. To learn more, check out [Flutter’s breaking change policy](https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes). + +## Conclusion + +A huge thank you to the entire Flutter community for making this release possible! Your contributions, feedback, and enthusiasm are what drive Flutter forward. We’re incredibly excited about the future of Flutter and can’t wait to see the amazing apps you build with Flutter 3.27. + +For a complete rundown of all the changes, check out the detailed [release notes and changelog](https://docs.flutter.dev/release/release-notes). Ready to experience the latest and greatest? Simply run `flutter upgrade` and start building! \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-3-29/images/04UHEVkh3Lnmhzzfa.webp b/sites/www/content/blog/whats-new-in-flutter-3-29/images/04UHEVkh3Lnmhzzfa.webp new file mode 100644 index 0000000000..58adb38b21 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-29/images/04UHEVkh3Lnmhzzfa.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-29/images/06P3X5mGDk25AoIW6.webp b/sites/www/content/blog/whats-new-in-flutter-3-29/images/06P3X5mGDk25AoIW6.webp new file mode 100644 index 0000000000..f21de44c25 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-29/images/06P3X5mGDk25AoIW6.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-29/images/09o5hT9CkjtAs9PsH.webp b/sites/www/content/blog/whats-new-in-flutter-3-29/images/09o5hT9CkjtAs9PsH.webp new file mode 100644 index 0000000000..2f01b1e46e Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-29/images/09o5hT9CkjtAs9PsH.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-29/images/0BcgxalxFMTEB_RFP.gif b/sites/www/content/blog/whats-new-in-flutter-3-29/images/0BcgxalxFMTEB_RFP.gif new file mode 100644 index 0000000000..1f87d4c5aa Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-29/images/0BcgxalxFMTEB_RFP.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-29/images/0HAZCXocwTzLyq3iO.webp b/sites/www/content/blog/whats-new-in-flutter-3-29/images/0HAZCXocwTzLyq3iO.webp new file mode 100644 index 0000000000..f4d82a3e7f Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-29/images/0HAZCXocwTzLyq3iO.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-29/images/0IhJCAxTPoV0EOc9I.webp b/sites/www/content/blog/whats-new-in-flutter-3-29/images/0IhJCAxTPoV0EOc9I.webp new file mode 100644 index 0000000000..7b0fc0aac1 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-29/images/0IhJCAxTPoV0EOc9I.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-29/images/0RP8usLpbhUNSCOhS.webp b/sites/www/content/blog/whats-new-in-flutter-3-29/images/0RP8usLpbhUNSCOhS.webp new file mode 100644 index 0000000000..831e38e053 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-29/images/0RP8usLpbhUNSCOhS.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-29/images/0Zz4XclkgqvYfadFw.webp b/sites/www/content/blog/whats-new-in-flutter-3-29/images/0Zz4XclkgqvYfadFw.webp new file mode 100644 index 0000000000..3210b555a8 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-29/images/0Zz4XclkgqvYfadFw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-29/images/0mIvmDgvoydaAWE05.webp b/sites/www/content/blog/whats-new-in-flutter-3-29/images/0mIvmDgvoydaAWE05.webp new file mode 100644 index 0000000000..e798b80dce Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-29/images/0mIvmDgvoydaAWE05.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-29/images/0u75a-VoDr43H_xxK.webp b/sites/www/content/blog/whats-new-in-flutter-3-29/images/0u75a-VoDr43H_xxK.webp new file mode 100644 index 0000000000..0010749304 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-29/images/0u75a-VoDr43H_xxK.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-29/images/0wkUFtSuMJHMV7VBJ.webp b/sites/www/content/blog/whats-new-in-flutter-3-29/images/0wkUFtSuMJHMV7VBJ.webp new file mode 100644 index 0000000000..72c8d3dad8 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-29/images/0wkUFtSuMJHMV7VBJ.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-29/images/1-XHpdhmBoEU-K3pSPWTX3g.webp b/sites/www/content/blog/whats-new-in-flutter-3-29/images/1-XHpdhmBoEU-K3pSPWTX3g.webp new file mode 100644 index 0000000000..d22be76ff8 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-29/images/1-XHpdhmBoEU-K3pSPWTX3g.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-29/images/1Mk6_aE2XEiK3Iq_H-q1L5Q.jpeg b/sites/www/content/blog/whats-new-in-flutter-3-29/images/1Mk6_aE2XEiK3Iq_H-q1L5Q.jpeg new file mode 100644 index 0000000000..fc1592348e Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-29/images/1Mk6_aE2XEiK3Iq_H-q1L5Q.jpeg differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-29/images/1R83UkKDD_CWQyHJCbiEUoQ.webp b/sites/www/content/blog/whats-new-in-flutter-3-29/images/1R83UkKDD_CWQyHJCbiEUoQ.webp new file mode 100644 index 0000000000..5c5a591134 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-29/images/1R83UkKDD_CWQyHJCbiEUoQ.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-29/images/1RfA62aWh5rI6v1S7zGzerw.webp b/sites/www/content/blog/whats-new-in-flutter-3-29/images/1RfA62aWh5rI6v1S7zGzerw.webp new file mode 100644 index 0000000000..5fc2bab42b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-29/images/1RfA62aWh5rI6v1S7zGzerw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-29/index.md b/sites/www/content/blog/whats-new-in-flutter-3-29/index.md new file mode 100644 index 0000000000..93cac4af01 --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-3-29/index.md @@ -0,0 +1,238 @@ +--- +title: "What’s new in Flutter 3.29" +description: "Enhancing Performance and Fidelity Across Platforms" +publishDate: 2025-02-12 +author: itsjustkevin +image: images/1-XHpdhmBoEU-K3pSPWTX3g.webp +category: releases +layout: blog +--- + +<DashImage figure src="images/1-XHpdhmBoEU-K3pSPWTX3g.webp" /> + + +## What’s new in Flutter 3.29 + +### Enhancing Performance and Fidelity Across Platforms + +## Introduction + +Dive into Flutter 3.29! This release refines development and boosts performance, with updates to Impeller, Cupertino, DevTools and more. With 104 unique authors contributing this release cycle, Flutter 3.29 showcases the community’s dedication. Let’s explore what’s new! + +## Framework + +### Cupertino updates + +`CupertinoNavigationBar` and `CupertinoSliverNavigationBar` now accept a [bottom widget](https://main-api.flutter.dev/flutter/cupertino/CupertinoNavigationBar/bottom.html), typically a search field or segmented control. + +In a `CupertinoSliverNavigationBar`, this bottom widget can be configured using the [bottomMode](https://main-api.flutter.dev/flutter/cupertino/CupertinoSliverNavigationBar/bottomMode.html) property to either automatically resize until hidden or to always be shown when the navigation bar is scrolled. + +Other changes to the navigation bars include: + +* When partially scrolled, [`CupertinoSliverNavigationBar`](https://main-api.flutter.dev/flutter/cupertino/CupertinoNavigationBar-class.html) snaps between its expanded and collapsed state. + +* A new [`CupertinoNavigationBar.large`](https://main-api.flutter.dev/flutter/cupertino/CupertinoNavigationBar/CupertinoNavigationBar.large.html) constructor allows the static navigation bar to display a large title. + +* Thanks to community contributor [davidhicks980](http://github.com/davidhicks980), Cupertino popups now have [more vibrant background blurs](https://github.com/flutter/flutter/pull/159272), which improves native fidelity. + +<DashImage figure src="images/09o5hT9CkjtAs9PsH.webp" /> + + +* The new [`CupertinoSheetRoute`](https://main-api.flutter.dev/flutter/cupertino/CupertinoSheetRoute-class.html) shows an iOS-styled modal sheet view that can be removed using the drag-to-dismiss gesture. + +* The new [`showCupertinoSheet`](https://main-api.flutter.dev/flutter/cupertino/showCupertinoSheet.html) function displays the sheet with a common setup for nested navigation baked in. + +<DashImage figure src="images/0wkUFtSuMJHMV7VBJ.webp" /> + + +* Community member [thejitenpatel](http://github.com/thejitenpatel) improved [`CupertinoAlertDialog`](https://api.flutter.dev/flutter/cupertino/CupertinoAlertDialog-class.html)’s native fidelity in dark mode, as shown in the following screenshots: + +<DashImage figure src="images/1R83UkKDD_CWQyHJCbiEUoQ.webp" alt="Before improvement — Left is a native view, right is a Flutter view" caption="Before improvement — Left is a native view, right is a Flutter view" /> + + +<DashImage figure src="images/1RfA62aWh5rI6v1S7zGzerw.webp" alt="After improvement — Left is a native view, right is a Flutter view" caption="After improvement — Left is a native view, right is a Flutter view" /> + + +* When inverted, Flutter’s text selection handles swap their order on iOS, and the border color of the text selection magnifier now matches the current theme. + +<DashImage figure src="images/0Zz4XclkgqvYfadFw.webp" /> + + +### Material + +* The [`FadeForwardsPageTransitionsBuilder`](https://main-api.flutter.dev/flutter/material/FadeForwardsPageTransitionsBuilder-class.html) is the new Material 3 (M3) page transition builder, designed to match the Android’s latest page transition behavior. During the transition, the incoming page slides in from right to left while simultaneously fading in, and the outgoing page slides out from right to left, fading out concurrently. This new transition also addresses the performance issues previously caused by the `ZoomPageTransitionsBuilder`. + +<DashImage figure src="images/04UHEVkh3Lnmhzzfa.webp" /> + + +* This release updates the `CircularProgressIndicator` and `LinearProgressIndicator` to align with the latest Material Design 3 specifications. To use the updated styling, either set the `year2023` property to `false`, or set [`ProgressIndicatorThemeData.year2023`](https://main-api.flutter.dev/flutter/material/ProgressIndicatorThemeData/year2023.html) to `false`. + +<DashImage figure src="images/0HAZCXocwTzLyq3iO.webp" /> + + +<DashImage figure src="images/0IhJCAxTPoV0EOc9I.webp" /> + + +* This release also introduces the latest Material 3 `Slider` design, featuring updated track and handle shapes. The `Slider` defaults to the previous Material 3 style. To enable the latest design, either set `year2023` to `false`, or set [`SliderThemeData.year2023`](https://main-api.flutter.dev/flutter/material/SliderThemeData/year2023.html) to `false`. + +<DashImage figure src="images/0RP8usLpbhUNSCOhS.webp" /> + + +This release includes several bug fixes and feature enhancements for the Material library. Notable fixes include: + +* Keyboard navigation now correctly triggers the [`DropdownMenu.onSelected`](https://main-api.flutter.dev/flutter/material/DropdownMenu/onSelected.html) callback. + +* Improved `TabBar` elastic tab animation. + +* Improved `RangeSlider` thumb alignment with divisions, thumb padding, and rounded corners. + +In addition to bug fixes, this release enhances the customizability of several Material components. The `mouseCursor` property has been added to `Chip`, `Tooltip`, and `ReorderableListView`, allowing for custom mouse cursors on hover. + +### Text selection + +Flutter now gives you more information about the selection under a `SelectionArea` or `SelectableRegion` with [`SelectionListener`](https://main-api.flutter.dev/flutter/widgets/SelectionListener-class.html) and `SelectionListenerNotifier`. The [`SelectionDetails`](https://main-api.flutter.dev/flutter/widgets/SelectionDetails-class.html) object, available through `SelectionListenerNotifier`, provides you with the start and end offsets of the selection (relative to the wrapped subtree), and indicates whether a selection exists and if it’s collapsed. For a widget or selectable subtree that you want to listen to, wrap it with the `SelectionListener` widget. + +Flutter also gives you information regarding the status of a `SelectionArea` or `SelectableRegion` through the [`SelectableRegionSelectionStatusScope`](https://main-api.flutter.dev/flutter/widgets/SelectableRegionSelectionStatusScope-class.html) inherited widget. You can check whether a parent `SelectionArea` or `SelectableRegion` is actively changing or has finalized its selection by using [`SelectableRegionSelectionStatusScope.maybeOf(context)`](https://main-api.flutter.dev/flutter/widgets/SelectableRegionSelectionStatusScope/maybeOf.html) and checking the [`SelectableRegionSelectionStatus`](https://main-api.flutter.dev/flutter/widgets/SelectableRegionSelectionStatus.html). + +<DashImage figure src="images/06P3X5mGDk25AoIW6.webp" /> + + +### Accessibility + +This release improves the accessibility of several Material widgets: + +* When a screen reader is enabled, the Form widget only announces the first error that it encounters. + +* A screen reader now announces the correct label for dropdown menus. + +## Web + +When initially released in May 2024, Flutter’s WebAssembly (wasm) support on the web required you to host your Flutter applications with special HTTP response headers. This requirement has now been relaxed. While updating the headers allows Flutter web apps built with wasm to run with multiple threads, using default headers now allows the application to run with wasm, but limited to a single thread. + +We have fixed several issues around images on the WebGL backends but the most important are: + +* [Decoding the images asynchronously off the UI thread to avoid jank](https://github.com/flutter/engine/pull/53201) + +* [Making Image.network work with CORS images out of the box](https://github.com/flutter/flutter/pull/157755) + +## Engine + +### Impeller Vulkan stability + +Thanks to the feedback and bug reports on the 3.27 beta and stable releases, a number of correctness and performance improvements were made to the Vulkan backend, including: + +* Fixed a reproducible flickering and visual jitter that many users reported on older Vulkan-capable devices. + +* Disabled Android Hardware Buffer swapchains. This feature has been placed behind a feature flag until it’s determined that it works correctly across Android devices. + +* A large number of black screen and crash reports were the result of using Vulkan on MediaTek/PowerVR soc’s and have been disabled. These devices now only use Impeller OpenGLES (see below). + +* Android emulators have been updated to use the Impeller GLES backend. + +### Impeller OpenGLES + +In 3.29, Android devices that don’t have a functional Vulkan driver will fall back to Impeller running on OpenGLES instead of using Skia. This behavior is enabled by default and doesn’t need to be configured. This brings Impeller support on Android to 100% of Flutter’s supported Android devices. + +### Impeller on iOS + +Skia support has been removed from the iOS backend and the `FLTEnableImpeller` opt-out flag no longer works. Further binary size reductions are expected in future releases as we begin to remove Skia components from iOS builds. + +### New features + +This release contains a number of new features that take advantage of the Impeller backend. + +### Backdrop filter optimizations + +Applications that display multiple backdrop filters can now use the new widget `BackdropGroup` and a new `BackdropFilter.grouped` constructor. These can improve performance of multiple blurs above and beyond what was possible on the Skia backend. For an example, visit the [basic.dart](https://github.com/flutter/flutter/blob/flutter-3.29-candidate.0/packages/flutter/lib/src/widgets/basic.dart#L502) source code. + +### ImageFilter.shader + +A new `ImageFilter` constructor allows applying a custom shader to any child widgets. This provides similar functionality to the `AnimatedSampler` widget from `package:flutter_shaders`, except that it also works with backdrop filters. For an example, visit the [painting.dart](https://github.com/flutter/flutter/blob/flutter-3.29-candidate.0/engine/src/flutter/lib/ui/painting.dart#L4239) source code. + +### Dart threading changes on Android/iOS + +Previously, Flutter executed Dart code on a different thread (UI thread) than the platform’s main thread. This allowed higher theoretical throughput if both platform and UI threads were occupied, but came at the cost of making most platform interop require serialized and asynchronous message passing. This architecture also had knock-on effects on text input, accessibility, plugins, and so on. + +Starting in 3.29, Flutter on Android and iOS execute Dart code on the application’s main thread, and there is no longer a separate UI thread. This is the first part in a series of changes to improve platform interop on mobile platforms, as it allows making synchronous calls to and from the platform without the overhead of serialization and message passing. + +## DevTools and IDEs + +### **New DevTools inspector** + +The new DevTools inspector is enabled by default for all users. The new inspector has a condensed widget tree, a new widget properties view, and an option to automatically update in response to hot-reload and navigation events. For more details, visit the [new inspector page](https://docs.flutter.dev/tools/devtools/inspector#new) on docs.flutter.dev. + +<DashImage figure src="images/0u75a-VoDr43H_xxK.webp" /> + + +### **Changes to on-device widget selection** + +After enabling widget selection mode from the DevTools inspector, any selections on the device are considered widget selections until you exit widget selection mode. Previously, after the initial widget selection you needed to click the on-device **Select widget** button to then select another widget. Instead, there is now an on-device button that can be used to quickly exit widget selection mode. + +<DashImage figure src="images/0BcgxalxFMTEB_RFP.gif" /> + + +### **Logging tool improvements** + +The Logging tool in DevTools has been improved in the following ways: + +* Logs include and display more metadata such as log severity, category, zone, and isolate. + +* Added support for filtering by log severity level. + +* Significant improvement in performance and initial load time. + +<DashImage figure src="images/0mIvmDgvoydaAWE05.webp" /> + + +These are just a few highlights from this release. To learn more about all the updates included in Flutter 3.29, check out the release notes for DevTools [2.41.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.41.0) and [2.42.2](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.42.2). + +## Breaking changes and deprecations + +### Discountinuing package support + +We plan to discontinue support for the following packages on April 30th, 2025: + +* ios_platform_images; see [ios_platform_images planned to be discontinued #162961](https://github.com/flutter/flutter/issues/162961) + +* css_colors; see [css_colors planned to be discontinued #162962](https://github.com/flutter/flutter/issues/162962) + +* palette_generator; see [palette_generator planned to be discontinued #162963](https://github.com/flutter/flutter/issues/162963) + +* flutter_image; see [flutter_image discontinued #162964](https://github.com/flutter/flutter/issues/162964) + +* flutter_adaptive_scaffold; see [flutter_adaptive_scaffold planned to be discontinued #162965](https://github.com/flutter/flutter/issues/162965) + +* flutter_markdown; see [flutter_markdown planned to be discontinued #162966](https://github.com/flutter/flutter/issues/162966) + +We encourage any interested members of the community to coordinate on establishing and maintaining forks. Links to the issues are provided above for discussion and coordination. + +### Removal of script-based application of the Flutter Gradle plugin + +Removal of script-based application of the Flutter Gradle plugin, which has been deprecated since 3.19. This supports converting the Flutter Gradle plugin from Groovy to Kotlin, and towards migrating it to use AGPs public API. This work is expected to decrease the frequency of breakages when a new AGP version is published and reduce build-based regressions. + +Projects created before 3.16 that have not yet migrated will likely be impacted. If the flutter tool logs the warning: “You are applying Flutter’s main Gradle plugin imperatively” when building your project, you are impacted and need to migrate according to [Deprecated imperative apply of Flutter’s Gradle plugins](https://docs.flutter.dev/release/breaking-changes/flutter-gradle-plugin-apply) on docs.flutter.dev. + +### Removal of the web HTML renderer + +This is the first release where the HTML renderer has been removed from Flutter web. For more information, visit [The intent to deprecate and remove the HTML renderer in Flutter web](https://docs.google.com/document/d/1DGamHsa2lz_Qtgfrfa3j3fRaEopJXc7tCFVM1TQlck8/edit?resourcekey=0-IjkqrCoo_EcRRS2bPYKI8w&tab=t.0) and[ Issue 145584: Intent to deprecate and remove the HTML renderer in Flutter web](https://github.com/flutter/flutter/issues/145954). + +### Web image handling + +This release also gives you [more control over how images are displayed on the web](https://github.com/flutter/flutter/pull/159917). In the previous beta release (3.28), Image widgets would automatically use `<img>` elements to display images from URLs when CORS errors occurred. This could lead to inconsistent behavior. Now, the `webHtmlElementStrategy` flag lets you choose when `<img>` elements are used. While this fallback is disabled by default, you can still enable the fallback or even prioritize `<img>` elements if needed for your application. + +### Material normalization + +As part of our ongoing theme normalization project in Material, this release deprecates `ThemeData.dialogBackgroundColor` in favor of `DialogThemeData.backgroundColor`. You can use the `dart fix` command to migrate affected code. + +Also in Material, the `ButtonStyleButton` `iconAlignment` property was deprecated after being added to `ButtonStyle` and associated `styleFrom` methods. + +For all [breaking changes](https://docs.flutter.dev/release/breaking-changes) in this release, see the full list of migration guidance on the breaking changes page of the website. + +As always, many thanks to the community for [contributing tests](https://github.com/flutter/tests/blob/master/README.md) — these help us identify breaking changes. To learn more, check out [Flutter’s breaking change policy](https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes). + +## **Conclusion** + +<DashImage figure src="images/1Mk6_aE2XEiK3Iq_H-q1L5Q.jpeg" /> + + +Thanks to the Flutter community for making this release possible! We’re excited for the future and can’t wait to see your apps. Check the [release notes and changelog](https://docs.flutter.dev/release/release-notes/release-notes-3.29.0), then run `flutter upgrade` and start building! \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-3-3/images/04EQPd4ednGIAhMwl.webp b/sites/www/content/blog/whats-new-in-flutter-3-3/images/04EQPd4ednGIAhMwl.webp new file mode 100644 index 0000000000..96896264fe Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-3/images/04EQPd4ednGIAhMwl.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-3/images/04Mac8uQL3ynXVIuD.webp b/sites/www/content/blog/whats-new-in-flutter-3-3/images/04Mac8uQL3ynXVIuD.webp new file mode 100644 index 0000000000..6aae51f9d7 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-3/images/04Mac8uQL3ynXVIuD.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-3/images/05fCsX84PRpjMtzx5.webp b/sites/www/content/blog/whats-new-in-flutter-3-3/images/05fCsX84PRpjMtzx5.webp new file mode 100644 index 0000000000..483110f668 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-3/images/05fCsX84PRpjMtzx5.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-3/images/0AxLlKvPfaiODtzch.webp b/sites/www/content/blog/whats-new-in-flutter-3-3/images/0AxLlKvPfaiODtzch.webp new file mode 100644 index 0000000000..1b07c28421 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-3/images/0AxLlKvPfaiODtzch.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-3/images/0SlsnQUfdOTijdsyF.webp b/sites/www/content/blog/whats-new-in-flutter-3-3/images/0SlsnQUfdOTijdsyF.webp new file mode 100644 index 0000000000..0002f84868 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-3/images/0SlsnQUfdOTijdsyF.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-3/images/0c4wovLXB8TpulHVU.webp b/sites/www/content/blog/whats-new-in-flutter-3-3/images/0c4wovLXB8TpulHVU.webp new file mode 100644 index 0000000000..703543b556 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-3/images/0c4wovLXB8TpulHVU.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-3/images/0dZTaQMQri0EI7HCw.webp b/sites/www/content/blog/whats-new-in-flutter-3-3/images/0dZTaQMQri0EI7HCw.webp new file mode 100644 index 0000000000..c00bdab5d8 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-3/images/0dZTaQMQri0EI7HCw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-3/images/0jVFZWFsCOuzeqzTb.webp b/sites/www/content/blog/whats-new-in-flutter-3-3/images/0jVFZWFsCOuzeqzTb.webp new file mode 100644 index 0000000000..187b27302d Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-3/images/0jVFZWFsCOuzeqzTb.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-3/images/0vFIZ4WL4UWSTrC27.webp b/sites/www/content/blog/whats-new-in-flutter-3-3/images/0vFIZ4WL4UWSTrC27.webp new file mode 100644 index 0000000000..01a0727456 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-3/images/0vFIZ4WL4UWSTrC27.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-3/index.md b/sites/www/content/blog/whats-new-in-flutter-3-3/index.md new file mode 100644 index 0000000000..253beae547 --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-3-3/index.md @@ -0,0 +1,204 @@ +--- +title: "What’s new in Flutter 3.3" +description: "Exciting updates for text handling, performance improvements, and more!" +publishDate: 2022-08-30 +author: itsjustkevin +image: images/04Mac8uQL3ynXVIuD.webp +category: releases +layout: blog +--- + +Welcome to **Flutter 3.3**! Only three months ago we announced Flutter 3, a massive milestone that included stable support for all platforms! The good news is that the momentum since this major release has not slowed down. Since the release of Flutter 3, Flutter has merged 5,687 pull requests. + +This release brings updates to Flutter web, desktop, text handling improvements in performance — and much more! + +We are also introducing exciting updates to the `go_router` package, DevTools, and VS Code extensions. Keep reading to learn more. + +## Framework + +### Global Selection + +Until now, Flutter web apps didn’t match the expected behavior when attempting to select text. Like Flutter apps, native web applications are composed of a tree of elements. In a traditional web app, you can select multiple web elements with a single sweeping gesture, something that couldn’t be easily done on a Flutter web app. + +Today that all changes! With the introduction of the `SelectableArea` widget, any child of the `SelectableArea` widget has selection enabled for free! + +<DashImage figure src="images/04Mac8uQL3ynXVIuD.webp" /> + + +To take advantage of this powerful new feature, simply wrap your route body (such as the `Scaffold`) with the `SelectionArea` widget and let Flutter do the rest. + +For a more comprehensive deep dive into this awesome new feature, please visit the [`SelectableArea` API](https://api.flutter.dev/flutter/material/SelectionArea-class.html) page. + +### Trackpad input + +Flutter 3.3 provides improved support for trackpad input. This not only provides richer and smoother control, but also reduces misinterpretation in certain cases. For an example of this misinterpretation, check out the [Drag a UI element](https://docs.flutter.dev/cookbook/effects/drag-a-widget) page in the [Flutter cookbook](https://docs.flutter.dev/cookbook). Scroll to the bottom of the page to get to the DartPad instance, and perform the following steps: + +1. Size the window smaller so that the upper part presents a scrollbar + +1. Hover over the upper part + +1. Use a trackpad to scroll + +1. Prior to installing Flutter 3.3, scrolling on a trackpad drags the item because Flutter was dispatching emulated general events + +1. After installing Flutter 3.3, scrolling on a trackpad correctly scrolls the list because Flutter is delivering the “scrolling” gesture, which isn’t recognized by the cards, but *is* recognized by the scroll view + +For more information, see the[ Flutter Trackpad Gesture](https://docs.google.com/document/d/1oRvebwjpsC3KlxN1gOYnEdxtNpQDYpPtUFAkmTUe-K8/edit?resourcekey=0-pt4_T7uggSTrsq2gWeGsYQ) design doc, and the following PRs on GitHub: + +* PR 89944: [Support trackpad gestures in framework](https://github.com/flutter/flutter/pull/89944) + +* PR 31591: [iPad trackpad gestures](https://github.com/flutter/engine/pull/31591) + +* PR 34060: [Re-land “ChromeOS/Android trackpad gestures”](https://github.com/flutter/engine/pull/34060) + +* PR 31594: [Win32 trackpad gestures](https://github.com/flutter/engine/pull/31594) + +* PR 31592: [Linux trackpad gestures](https://github.com/flutter/engine/pull/31592) + +* PR 31593: [Mac trackpad gesturesmacOS](https://github.com/flutter/engine/pull/31593) + +### Scribble + +Thanks to the amazing contributions of community member [fbcouch](https://github.com/fbcouch), Flutter now supports [Scribble](https://support.apple.com/guide/ipad/enter-text-with-scribble-ipad355ab2a7/ipados) handwriting input using the Apple Pencil on iPadOS. This feature is enabled by default on `CupertinoTextField`, `TextField`, and `EditableText`. To enable this feature for your end users, simply upgrade to Flutter 3.3. + +<DashImage figure src="images/0SlsnQUfdOTijdsyF.webp" /> + + +### Text input + +To improve support for rich text editing, this release introduces the ability to receive granular text updates from the platform’s `TextInputPlugin`. Previously, the `TextInputClient` only delivered the new editing state with no delta between the old and new, `TextEditingDeltas` and the `DeltaTextInputClient` fill this information gap. Having access to these deltas allows you to build an input field with styled ranges that expand and contract as you type. To learn more, check out the [Rich Text Editor demo](https://flutter.github.io/samples/rich_text_editor.html). + +## Material Design 3 + +The Flutter team continues to migrate more Material Design 3 components to Flutter. This release includes updates to [`IconButton`](https://api.flutter.dev/flutter/material/IconButton-class.html), [`Chips`](https://api.flutter.dev/flutter/material/Chip-class.html), and large and medium variants for [`AppBar`](https://api.flutter.dev/flutter/material/AppBar-class.html). + +To monitor the progress on the Material Design 3 migration, check out [Bring Material 3 to Flutter](https://github.com/flutter/flutter/issues/91605) on GitHub. + +### IconButton + +<DashImage figure src="images/0AxLlKvPfaiODtzch.webp" /> + + +### Chip + +<DashImage figure src="images/05fCsX84PRpjMtzx5.webp" /> + + +### Medium and large AppBar + +<DashImage figure src="images/0jVFZWFsCOuzeqzTb.webp" /> + + +<DashImage figure src="images/0c4wovLXB8TpulHVU.webp" /> + + +## Desktop + +### Windows + +Previously, the Windows desktop application’s version was set by a file specific to the Windows application. This behavior was inconsistent with the way other platforms set their versions. + +Windows desktop application versions can now be set from your projects `pubspec.yaml` file and build arguments. This makes it easier to enable auto updates for your end customers to get the latest and greatest when an application update is pushed. + +For more information on setting your applications version, follow the documentation on [docs.flutter.dev](https://docs.flutter.dev/deployment/windows#updating-the-apps-version-number) and the [migration guide](https://docs.flutter.dev/development/platform-integration/windows/version-migration). Projects created before Flutter 3.3 need to be updated to get this feature. + +## Packages + +### go_router + +When designing apps with complex navigation needs, things can get pretty hard to wrap your head around. To extend Flutter’s native navigation API, the team has published a new version of the `go_router` package, making it simpler to design routing logic that works across mobile, desktop, and the web. + +The [`go router`](https://pub.dev/packages/go_router) package, maintained by the Flutter team, simplifies routing by providing a declarative, url-based API, making it easier to navigate and handle deep-links. The latest version (4.3) enables apps to redirect using asynchronous code, and includes other breaking changes described in the [migration guide](https://docs.google.com/document/d/10l22o4ml4Ss83UyzqUC8_xYOv_QjZEi80lJDNE4q7wM/edit?usp=sharing&resourcekey=0-U-BXBQzNfkk4v241Ow-vZg). + +For more information, check out the [Navigation and routing](https://docs.flutter.dev/development/ui/navigation) page on docs.flutter.dev. + +## VS Code extension enhancements + +The Visual Studio Code extension for Flutter has several updates including improvements for adding dependencies. You can now add multiple, comma-separated dependencies in one step using **Dart: Add Dependency**. + +<DashImage figure src="images/0vFIZ4WL4UWSTrC27.webp" /> + + +You can read about the Visual Studio Code extension enhancements made since the last Flutter stable release at: + +* [VS Code extensions v3.46](https://groups.google.com/g/flutter-announce/c/u1iSDMtKMVg) + +* [VS Code extensions v3.44](https://groups.google.com/g/flutter-announce/c/x4m9o93-Dng) + +* [VS Code extensions v3.42](https://groups.google.com/g/flutter-announce/c/45Wsk5pISx4) + +## Flutter DevTools updates + +DevTools comes with a number of updates since the last stable Flutter release, including UX and performance improvements to data display tables for faster, less jittery scrolling of large lists of events ([#4175](https://github.com/flutter/devtools/pull/4175)). + +For the complete list of updates since Flutter 3.0, check out the individual announcements here: + +* [Flutter DevTools 2.16.0 release notes](https://docs.flutter.dev/development/tools/devtools/release-notes/release-notes-2.16.0) + +* [Flutter DevTools 2.15.0 release notes](https://docs.flutter.dev/development/tools/devtools/release-notes/release-notes-2.15.0) + +* [Flutter DevTools 2.14.0 release notes](https://docs.flutter.dev/development/tools/devtools/release-notes/release-notes-2.14.0) + +## Performance + +### Raster cache improvements + +This release increases the performance of loading images from assets by eliminating copies and reducing Dart garbage collection (GC) pressure. Previously, when loading asset images, the `ImageProvider` API required the compressed data to be copied multiple times. First, it was copied into the native heap when opening the asset and exposing it to Dart as a typed data array. Then, it was copied a second time when that typed data array was copied to the internal storage of a `ui.ImmutableBuffer`. + +With [the addition](https://github.com/flutter/engine/pull/32999) of `ui.ImmutableBuffer.fromAsset`, compressed image bytes can be loaded directly into the structure used for decoding. This approach [requires changes](https://github.com/flutter/flutter/pull/103496) to the byte loading pipeline of `ImageProviders`. This process is also faster, because it bypasses some additional scheduling overhead required bythe previous method channel-based loader. In particular, image loading time improved by nearly 2x in our microbenchmarks. + +<DashImage figure src="images/0dZTaQMQri0EI7HCw.webp" /> + + +For more information and a migration guide, see [Adding ImageProvider.loadBuffer](https://docs.flutter.dev/release/breaking-changes/image-provider-load-buffer) on docs.flutter.dev. + +## Stability + +### iOS pointer compression disabled + +In the 2.10 stable release, we enabled Dart’s [pointer compression optimization](https://medium.com/dartlang/dart-2-15-7e7a598e508a#0c15) on iOS. However, [Yeatse](https://github.com/Yeatse), on GitHub, [alerted us](https://github.com/flutter/flutter/issues/105183) to an unintended consequence of the optimization. Dart’s pointer compression works by reserving a large virtual memory region for Dart’s heap. Since the total virtual memory allocation allowed on iOS is less than on other platforms, this large reservation reduces the amount of memory available for use by other components that reserve their own memory, for example, Flutter plugins. + +While disabling pointer compression increases the memory consumed by Dart objects, it also increases the memory *available* for non-Dart parts of a Flutter application, which is more desirable overall. + +Apple provides an entitlement that can increase the maximum allowed virtual memory allocation for an application, however this entitlement is only supported on newer iOS versions, and wouldn’t work on devices running versions of iOS that Flutter still supports. When we are able to use this entitlement everywhere, we intend to revisit this optimization. + +## API improvements + +### PlatformDispatcher.onError + +In previous releases, you had to manually configure a custom `Zone` in order to catch all of an application’s exceptions and errors. However, custom `Zone`s were detrimental to a number of optimizations in Dart’s core libraries, which slowed down application start-up time. **In this release, instead of using a custom `Zone`, you should catch all errors and exceptions by setting the `PlatformDispatcher.onError` callback.** For more information, check out the updated [Handling errors in Flutter](https://docs.flutter.dev/testing/errors) page on docs.flutter.dev. + +### FragmentProgram changes + +Fragment shaders written in GLSL and listed under a `shaders:` section in the Flutter manifest of an application’s `pubspec.yaml` file will now be automatically compiled to the correct format understood by the Engine and bundled with the application as an asset. Thanks to this change, you will no longer have to manually compile shaders using third-party tools. Going forward, you should regard the Engine’s `FragmentProgram` API as accepting only the output of Flutter’s build tooling. This isn’t yet the case, but this change is planned for a future release, as described in the [FragmentProgram API Support improvements](http://flutter.dev/go/fragment-program-support) design doc. + +For an example of this change, see this [Flutter shader example](https://github.com/zanderso/fragment_shader_example). + +### Fractional translation + +Previously, the Flutter Engine would always align composited layers to exact pixel boundaries because it improved rendering performance on older (32-bit) model iPhones. Since adding desktop support, we’ve noticed that this led to observable snapping behavior, since screen device pixel ratios are commonly much lower. For example, on low DPR screens, tooltips could be seen visibly snapping when fading in. After determining that this pixel snapping was no longer necessary for performance on newer iPhone models, [we’ve removed this pixel snapping](https://github.com/flutter/flutter/issues/103909) from the Flutter Engine to improve desktop fidelity. Additionally, we also found that removing this pixel snapping stabilized a number of our golden image tests, which would frequently change with small hairline rendering differences. + +## Changes to supported platforms + +### 32-bit iOS deprecation + +As we announced earlier with the 3.0 stable release, due to decreased usage, that release was the [last one to support 32-bit iOS devices and iOS versions 9 and 10](http://flutter.dev/go/rfc-32-bit-ios-unsupported). This change affects the iPhone 4S, iPhone 5, iPhone 5C, and the 2nd, 3d, and 4th generation iPad devices. The 3.3 stable version of Flutter and all following stable releases no longer support 32-bit iOS devices and iOS versions 9 and 10. This means that apps built against Flutter 3.3 and later won’t run on those devices. + +### Sunsetting macOS 10.11 and 10.12 + +In the Q4 2022 stable release, we expect to drop support for macOS versions 10.11 and 10.12. This means that apps built against stable Flutter SDKs after that point will no longer work on these versions, and the minimum macOS version supported by Flutter will increase to 10.13 High Sierra. + +### Bitcode deprecation + +Bitcode [will no longer be accepted for iOS app submission in the upcoming Xcode 14 release](https://developer.apple.com/documentation/xcode-release-notes/xcode-14-release-notes), and projects with bitcode enabled will emit a build warning in this version of Xcode. In light of this, Flutter will drop support for bitcode in a future stable release. + +By default, Flutter apps don’t have bitcode enabled, and we don’t expect this to impact many developers. However, if you have enabled bitcode manually in your Xcode project, disable it as soon as you upgrade to Xcode 14. You can do so by opening `ios/Runner.xcworkspace` and setting the build setting **Enable Bitcode** to **No**. Add-to-app developers should disable it in the host Xcode project. + +<DashImage figure src="images/04EQPd4ednGIAhMwl.webp" /> + + +See [Apple’s documentation](https://help.apple.com/xcode/mac/11.0/index.html?localePath=en.lproj#/devde46df08a) to learn more about bitcode distribution. + +## Summary + +As we always say, the Flutter team at Google is extremely grateful for the incredible work the community puts in to make Flutter the wonderful experience that it is! We look forward to continuing to iterate on the work that has been done, focusing on what matters most — all of you! \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/images/0H9mW79LCQEJBJ_i2.webp b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0H9mW79LCQEJBJ_i2.webp new file mode 100644 index 0000000000..4927259642 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0H9mW79LCQEJBJ_i2.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/images/0HlInLfTOQSZ3n-NG.webp b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0HlInLfTOQSZ3n-NG.webp new file mode 100644 index 0000000000..e96ab7303a Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0HlInLfTOQSZ3n-NG.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/images/0JjkFrjtxjh5tQmql.webp b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0JjkFrjtxjh5tQmql.webp new file mode 100644 index 0000000000..5c42c5d57c Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0JjkFrjtxjh5tQmql.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/images/0KoBkOexQ6DyQdnQt.webp b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0KoBkOexQ6DyQdnQt.webp new file mode 100644 index 0000000000..4508e5920f Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0KoBkOexQ6DyQdnQt.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/images/0NIsvxkNdRcbgtLdk.gif b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0NIsvxkNdRcbgtLdk.gif new file mode 100644 index 0000000000..1096ae6cc0 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0NIsvxkNdRcbgtLdk.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/images/0R1tsiciz-w7OdOjr.webp b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0R1tsiciz-w7OdOjr.webp new file mode 100644 index 0000000000..27027498cc Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0R1tsiciz-w7OdOjr.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/images/0YCBMHZS_mi2vJ92S.webp b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0YCBMHZS_mi2vJ92S.webp new file mode 100644 index 0000000000..c75e72cfae Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0YCBMHZS_mi2vJ92S.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/images/0ZbSuN_xSc7pXkUyC.webp b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0ZbSuN_xSc7pXkUyC.webp new file mode 100644 index 0000000000..c4af2baca1 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0ZbSuN_xSc7pXkUyC.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/images/0d7zwnDUZqn9bgnoS.webp b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0d7zwnDUZqn9bgnoS.webp new file mode 100644 index 0000000000..6361b214a6 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0d7zwnDUZqn9bgnoS.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/images/0nFVGZ5PG6gTqc_F7.webp b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0nFVGZ5PG6gTqc_F7.webp new file mode 100644 index 0000000000..afda302bc3 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0nFVGZ5PG6gTqc_F7.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/images/0nT-5bpsRVyZELaTq.webp b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0nT-5bpsRVyZELaTq.webp new file mode 100644 index 0000000000..50b88763ca Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0nT-5bpsRVyZELaTq.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/images/0s1EWYhGX9j4qOWB1.webp b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0s1EWYhGX9j4qOWB1.webp new file mode 100644 index 0000000000..80638e366b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0s1EWYhGX9j4qOWB1.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/images/0uHuGHM5dYCTkF_Vz.webp b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0uHuGHM5dYCTkF_Vz.webp new file mode 100644 index 0000000000..c74f8414cd Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0uHuGHM5dYCTkF_Vz.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/images/0uqbdwtRR2oryyhJy.webp b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0uqbdwtRR2oryyhJy.webp new file mode 100644 index 0000000000..671713fe21 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0uqbdwtRR2oryyhJy.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/images/0yGjm9dQU4oCG5XwG.webp b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0yGjm9dQU4oCG5XwG.webp new file mode 100644 index 0000000000..bacd32fbd2 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-32/images/0yGjm9dQU4oCG5XwG.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/images/151UZbL3Qb7BGDEuujTTg6A.gif b/sites/www/content/blog/whats-new-in-flutter-3-32/images/151UZbL3Qb7BGDEuujTTg6A.gif new file mode 100644 index 0000000000..80a57f415b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-32/images/151UZbL3Qb7BGDEuujTTg6A.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/images/1FTpRgnrZGMzzCjEl4sCmiw.webp b/sites/www/content/blog/whats-new-in-flutter-3-32/images/1FTpRgnrZGMzzCjEl4sCmiw.webp new file mode 100644 index 0000000000..a33af8d960 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-32/images/1FTpRgnrZGMzzCjEl4sCmiw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/images/1iEWYohD1vd7FMpPWvK2__A.webp b/sites/www/content/blog/whats-new-in-flutter-3-32/images/1iEWYohD1vd7FMpPWvK2__A.webp new file mode 100644 index 0000000000..463fd3c269 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-32/images/1iEWYohD1vd7FMpPWvK2__A.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-32/index.md b/sites/www/content/blog/whats-new-in-flutter-3-32/index.md new file mode 100644 index 0000000000..30379643ea --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-3-32/index.md @@ -0,0 +1,408 @@ +--- +title: "What’s new in Flutter 3.32" +description: "Hot reload on web, native fidelity, and deeper integrations" +publishDate: 2025-05-20 +author: itsjustkevin +image: images/1FTpRgnrZGMzzCjEl4sCmiw.webp +category: releases +layout: blog +--- + +<DashImage figure src="images/1FTpRgnrZGMzzCjEl4sCmiw.webp" /> + + +## What’s new in Flutter 3.32 + +### Hot reload on web, native fidelity, and deeper integrations + +## Introduction + +Welcome to Flutter 3.32! This release is packed with features designed to accelerate your development and enhance your apps. Get ready for hot reload on the web, stunning Cupertino squircles for native fidelity, and powerful new AI integrations with Firebase. With 1024 commits from 156 unique contributors, including 39 first time contributors, Flutter 3.32 is a testament to our community’s incredible work. Let’s explore what’s new! + +## Web + +### Hot reload on the web (Experimental) + +We’re excited to announce that you can now hot reload on the web! Web support for hot reload is the [#2 most voted issue](https://github.com/flutter/flutter/issues/53041) on the Flutter tracker. We’re excited to give you a chance to try it out on your own projects! We want your help to make sure this exciting new feature has everything developers want from it. + +If you discover any issues, file a bug using Dart’s [Web Hot Reload issue template](https://github.com/dart-lang/sdk/issues/new?template=6_web_hot_reload.yml). You can find a list of known issues on the [Web Hot Reload Known Issues](https://github.com/orgs/dart-lang/projects/107/views/1) GitHub project. + +### **Now the fun part: how to use the feature** + +We’ve added a simple command line flag `--web-experimental-hot-reload` that you can pass to Flutter anywhere you invoke `flutter run`. + +### **Running from VS Code** + +If you use debug configurations in VS Code, you can add this extra configuration to your [launch.json file](https://code.visualstudio.com/docs/editor/debugging-configuration): + +```bash +"configurations": [ +… + { + "name": "Flutter for web (hot reloadable)", + "type": "dart", + "request": "launch", + "program": "lib/main.dart", + "args": [ + "-d", + "chrome", + "--web-experimental-hot-reload", + ] + } +] +``` + + +For best results, we recommend enabling the “[Dart: Flutter Hot Reload On Save](https://code.visualstudio.com/docs/editor/settings)” setting in VS Code. A hot reload can also be triggered with the ⚡icon in the Run/Debug panel. Hot restarts can still be triggered by using the ⟳ button. + +### **Running from the command line** + +If you use “flutter run” on the command line, you can now run hot reload on the web with the following: + +`flutter run -d chrome --web-experimental-hot-reload` + +When hot reload is enabled, you can reload your application by pressing “r” in the running terminal, or “R” to hot restart. + +### **Reloading in DartPad** + +Hot reload is also now available on [DartPad](https://dartpad.dev/) with a new **Reload** button. The feature is only available if Flutter is detected in the running application. You can try it using a[ sample app](https://dartpad.dev/?sample=counter&channel=main) provided by DartPad. + +Thanks for taking the time to help us make Hot Reload on the web amazing! + +## Framework + +We have continued our effort to move logic from the Material library to the widgets library. + +This release introduces a new [`Expansible`](https://main-api.flutter.dev/flutter/widgets/Expansible-class.html) widget that makes it easier to create widgets that expand and collapse with different visual themes. This new widget serves as the underlying building block for the Material-styled [`ExpansionTile`](https://main-api.flutter.dev/flutter/material/ExpansionTile-class.html). + +Another valuable addition in this release is [`RawMenuAnchor`](https://main-api.flutter.dev/flutter/widgets/RawMenuAnchor-class.html), contributed by community member [@davidhicks980](http://github.com/davidhicks980). This widget now serves as the underlying building block for the Material-styled [`MenuAnchor`](https://main-api.flutter.dev/flutter/material/MenuAnchor-class.html), paving the way for the creation of menus with diverse visual themes. Furthermore, `RawMenuAnchor` can be used independently as an unstyled menu, granting you full control over its appearance. + +<DashImage figure src="images/0uHuGHM5dYCTkF_Vz.webp" /> + + +Refactors like these add better support for core widgets to be used in the implementation of design languages like Cupertino and Material (and more!) with more consistent surfaces and code sharing. + +### Cupertino + +**Squircles** + +We’re pleased to introduce a significant new addition to Flutter’s shape capabilities: the rounded superellipse. + +<DashImage figure src="images/0NIsvxkNdRcbgtLdk.gif" /> + + +Often referred to as the “Apple squircle”, this shape is a cornerstone of the iOS design language, known for its smoother and more continuous curves compared to traditional rounded rectangles. Support for this shape was a frequent request from the Flutter community. Its inclusion represents another step in our efforts to provide high-fidelity UI with native look and feel on Apple platforms. + +Both `CupertinoAlertDialog` and `CupertinoActionSheet` have been updated to use this new shape! + +You can add rounded superellipses to your applications using these new APIs: + +1. [`RoundedSuperellipseBorder`](https://main-api.flutter.dev/flutter/painting/RoundedSuperellipseBorder-class.html) for painting or as a widget shape + +1. [`ClipRSuperellipse`](https://main-api.flutter.dev/flutter/widgets/ClipRSuperellipse-class.html) for clipping. + +1. `[Canvas.drawRSuperellipse](https://main-api.flutter.dev/flutter/dart-ui/Canvas/drawRSuperellipse.html), [Canvas.clipRSuperellipse](https://main-api.flutter.dev/flutter/dart-ui/Canvas/clipRSuperellipse.html)`, and [`Path.addRSuperellipse`](https://main-api.flutter.dev/flutter/dart-ui/Path/addRSuperellipse.html) for lower-level APIs that offer more direct control. + +Please note that the rounded superellipse is under active development. Currently, it’s only supported on iOS and Android, otherwise the behavior falls back to a standard rounded rectangle. Performance optimizations are also ongoing. We encourage you to explore this new feature with these considerations in mind, and we look forward to enhancing its capabilities in future updates! + +**Sheet** + +There were also several issues addressed with the Cupertino sheet, including fixing how the sheet sets the system UI theme when it opens on Android. + +The sheet was adjusted to fix the navigation bar’s height and ensure content isn’t cut off at the bottom. The following screenshot demonstrates before and after this change. + +<DashImage figure src="images/0JjkFrjtxjh5tQmql.webp" /> + + +<DashImage figure src="images/0YCBMHZS_mi2vJ92S.webp" /> + + +Other fixes in this release include an issue where the sheet transition was incompatible with `PopupMenuButton`, and the rounded corner transition of the sheet was improved to look correct on more devices. Previously, it looked incorrect on some screens with different corner radii. + +You can also now use the new [`enableDrag`](https://main-api.flutter.dev/flutter/cupertino/CupertinoSheetRoute/enableDrag.html) argument on [`CupertinoSheetRoute`](https://main-api.flutter.dev/flutter/cupertino/CupertinoSheetRoute-class.html) and [`showCupertinoSheet`](https://main-api.flutter.dev/flutter/cupertino/showCupertinoSheet.html) to disable the Cupertino bottom sheet’s drag-down-to-dismiss behavior. ([#163923](https://github.com/flutter/flutter/pull/163923)**)** + +<DashImage figure src="images/1iEWYohD1vd7FMpPWvK2__A.webp" /> + + +**Navigation bars** + +[`CupertinoSliverNavigationBar.search`](https://main-api.flutter.dev/flutter/cupertino/CupertinoSliverNavigationBar/CupertinoSliverNavigationBar.search.html) sees fidelity improvements to its animation while opening or closing the search view, as well as correct alignment of the search field’s prefix and suffix icons. + +<DashImage figure src="images/0R1tsiciz-w7OdOjr.webp" /> + + +Finally, the transition between routes with `CupertinoNavigationBars` or `CupertinoSliverNavigationBars` has been updated to match the latest iOS transitions ([#164956](https://github.com/flutter/flutter/pull/164956)). + +### Material + +This release brings a range of feature enhancements and bug fixes to the Material library, improving both functionality and developer experience. + +The feature enhancements include: + +* The `CarouselController` provides a more convenient [`animateToIndex`](https://github.com/flutter/flutter/pull/162694) method, enabling a smooth, index-based navigation for carousels, whether with fixed- or dynamically-sized items using `flexWeights`. + +<DashImage figure src="images/0nT-5bpsRVyZELaTq.webp" /> + + +* TabBar now has [`onHover`](https://main-api.flutter.dev/flutter/material/TabBar/onHover.html) and [`onFocusChange`](https://main-api.flutter.dev/flutter/material/TabBar/onFocusChange.html) callbacks, giving you more control over the widget’s appearance in different states. + +<DashImage figure src="images/0nFVGZ5PG6gTqc_F7.webp" /> + + +* `SearchAnchor` and `SearchAnchor.bar` now include [`viewOnOpen` and `onOpen`](https://github.com/flutter/flutter/pull/164541) callbacks respectively, allowing for better observation and handling of open/close events. + +<DashImage figure src="images/0H9mW79LCQEJBJ_i2.webp" /> + + +* [`CalendarDatePicker`](https://main-api.flutter.dev/flutter/material/CalendarDatePicker-class.html) now accepts a [`calendarDelegate`](https://main-api.flutter.dev/flutter/material/CalendarDatePicker/calendarDelegate.html) to enable the integration of custom calendar logic beyond the Gregorian Calendar System. The example demonstrates a hypothetical calendar system where even-numbered months have 21 days, odd-numbered months have 28 days, and every month starts on a Monday, showcasing the flexibility this delegate offers. + +<DashImage figure src="images/0uqbdwtRR2oryyhJy.webp" /> + + +Other minor enhancements include adding [`animationStyle`](https://github.com/flutter/flutter/pull/164002) to `showDialog`, `showAdaptiveDialog`, and `DialogRoute` to customize the animation when the dialog is opened and closed; `Divider` now accepts a [`borderRadius`](https://main-api.flutter.dev/flutter/material/Divider/radius.html) to allow customizing the border of the divider, especially when the divider is thick. + +Beyond the new features, this release also addresses several bugs related to `DropdownMenu` and `Slider`s in the Material library: + +* The `DropdownMenu` widget now allows its menu width to be smaller than the text field. The example has been updated to resolve a previous `RenderFlex` overflow error. The default width of `DropdownMenu` now respects the width of the text label in the text field. + +* When hovering over a `RangeSlider` thumb, only the overlay of the hovered thumb appears. Previously, both overlays would incorrectly show up. The `Slider` thumb can now reach both ends of the track when the track color is transparent, overcoming a prior limitation where it could not. + +### Accessibility + +**Optimized [semantics tree compiling](https://github.com/flutter/flutter/pull/161195)** + +This feature reduced semantics compilation time by ~80%. In Flutter for web, this translates to 30% reduction in frame time when semantics is enabled. + +**Introducing finely-grained semantics roles** + +A new [`SemanticsRole`](https://api.flutter.dev/flutter/dart-ui/SemanticsRole.html) API has been integrated into the `Semantics` widget and its associated components, offering developers more precise control over how UI elements are interpreted by assistive technologies. This enhancement allows for the assignment of specific roles to an entire subtree of widgets by simply wrapping it with a `Semantics` widget configured with the desired role. + +A list of available roles is detailed in the [official API documentation](https://api.flutter.dev/flutter/dart-ui/SemanticsRole.html). Currently, this advanced semantics role functionality is available for web-based applications, with support for other platforms coming in future releases. + +For more information, see [Enhancing accessibility with semantic roles](https://docs.flutter.dev/ui/accessibility-and-internationalization/accessibility#enhancing-accessibility-with-semantic-roles). + +**Other accessibility improvements** + +* **Improved user experience for widgets and screen readers:** Better accessibility support and user experience across various widgets, including text fields, focus handling, menus, sliders, and dropdowns. Screen readers now provide more descriptive feedback when users interact with these elements. + +* **Smoother web-focus navigation with semantics:** Refined web focus behavior when semantics are enabled, significantly reducing abrupt focus jumps between widgets for a more intuitive navigation experience. + +* **Improved link recognition for Android TalkBack:** Android TalkBack now correctly identifies and announces links defined using `Semantics.linkUrl` or the `Link` widget from the `url_launcher` package. + +* **Windows high-contrast mode support in Flutter for web:** Introduced support for Windows’ “forced colors” mode (often used in high contrast scenarios). Developers can now set the `useSystemColors` boolean in `ThemeData` to automatically apply system colors to Flutter themes, enhancing visibility for users who rely on these settings. + +* **Refined iOS Voice Control experience:** Improved the user experience for iOS Voice Control by ensuring that non-actionable widgets no longer display unnecessary labels, leading to a cleaner and more focused interaction for users. + +### Text input + +Text input had several improvements in this release: + +1. The system text selection context menu was launched on iOS, which is referenced in the iOS section below. + +1. The layout of the `Autocomplete` widget’s options were ported to `OverlayPortal`, improving performance and fixing layout bugs. + +1. It’s now possible to customize `onTapUpOutside` behavior in text fields ([#162575](https://github.com/flutter/flutter/pull/162575)). + +1. Developers can now generate any widget they want as an error message for `FormField` instead of just error text ([#162255](https://github.com/flutter/flutter/pull/162255)). + +1. Selectable text in Flutter became less buggy ([#162228](https://github.com/flutter/flutter/pull/162228)) and more performant on web ([#161682](https://github.com/flutter/flutter/pull/161682)). + +### Progress on multi-window support + +Contributors from Canonical have made excellent progress towards allowing desktop applications to have multiple windows! + +Canonical fixed several features that were broken when an app has multiple windows: + +1. Accessibility: [#164577](https://github.com/flutter/flutter/pull/164577) + +1. App lifecycle notifications: [#164872](https://github.com/flutter/flutter/pull/164872) + +1. Focus: [#164296](https://github.com/flutter/flutter/pull/164296) + +1. Keyboard events: [#162131](https://github.com/flutter/flutter/pull/162131), [#163962](https://github.com/flutter/flutter/pull/163962) + +1. Text input: [#163847](https://github.com/flutter/flutter/pull/163847), [#164014](https://github.com/flutter/flutter/pull/164014) + +1. Mouse events: [#163855](https://github.com/flutter/flutter/pull/163855) + +Canonical also added a feature that lets Dart code FFI communicate directly to the Flutter engine ([#163430](https://github.com/flutter/flutter/issues/163430)). This lays the foundation for Flutter’s future windowing APIs. + +Finally, Canonical introduced a raster thread on Linux ([#161879](https://github.com/flutter/flutter/pull/161879)). This improves frame throughput, ensuring Flutter Linux remains buttery smooth even if you have multiple windows. + +### Desktop thread merging + +Canonical also updated Windows and macOS to allow apps to merge the UI and platform threads ([#162883](https://github.com/flutter/flutter/pull/162883), [#162935](https://github.com/flutter/flutter/pull/162935)). + +Merged threads let you use Dart FFI to interoperate with native APIs that must be called on the platform thread. For example, if you enable merged threads on Windows you can use Dart FFI to resize your app’s window using win32 APIs! + +On Windows, you can turn on merged threads by adding the following to your `windows/runner/main.cpp` file, inside the `wWinMain` method: + +```less +project.set_ui_thread_policy(flutter::UIThreadPolicy::RunOnPlatformThread) +``` + + +On macOS, you can turn on merged threads by adding the following to your `macos/Runner/Info.plist` file, inside the `<dict>` element: + +```xml +<key>FLTEnableMergedPlatformUIThread</key> +<true /> +``` + + +In a future release, we will turn on merged threads by default on Windows and macOS. Please try this out and [create issues](https://github.com/flutter/flutter/issues/new/choose) if you run into any bugs! + +## iOS + +We’ve enhanced the pasting experience in Flutter apps on iOS! For basic text fields without custom actions, users will no longer see a confirmation dialog when they paste content from other apps. This is now enabled by default on all Flutter iOS apps. Note, if your app uses custom actions (for example, “Send Email” in context menus), this feature is [not yet supported](https://github.com/flutter/flutter/issues/140184). + +## Android + +### Kotlin language rewrite of Gradle tooling + +Flutter’s Gradle plugin has been converted from Groovy to Kotlin. This transition to Kotlin makes the codebase easier to contribute to, and also facilitates the introduction of unit tests for the plugin. The added tests, as well as the conversion from a dynamically typed and executed language to a statically typed one, should result in a more stable and reliable build process for Flutter developers. We don’t expect any behavior changes as a result of the rewrite, so please [file an issue](https://github.com/flutter/flutter/issues/new/choose) if you notice Android builds behaving differently than you expect. + +### Scribe / Stylus support + +You can now use your stylus to write into text fields on Android, in the same way that Apple Pencil handwriting input already works on Flutter iOS apps. Users can start writing directly on top of any Flutter text input field, and the handwriting will appear as text in the field. Not all gestures are supported yet, but we are [working to close those gaps](https://github.com/flutter/flutter/issues/156018). This feature is supported on Android 14 and above and can be disabled, if needed, with the[`TextField.stylusHandwritingEnabled`](https://api.flutter.dev/flutter/material/TextField/stylusHandwritingEnabled.html) or [`CupertinoTextField.stylusHandwritingEnabled`](https://api.flutter.dev/flutter/cupertino/CupertinoTextField/stylusHandwritingEnabled.html) parameters. + +## Engine + +### Impeller on Android + +Beginning from the 3.29.3 release, on devices running Android API level 28 (Android 9) and older, Flutter apps will use the legacy Skia renderer. This change improves the stability of Flutter on older Android devices. Impeller continues to be the default renderer on devices running API level 29 (Android 10) and newer. + +As we get more feedback from users using different devices, we have adjusted which devices use Impeller’s Vulkan versus OpenGLES backend. In particular, in this release the following devices will use OpenGLES instead of Vulkan: [the Android emulator](https://github.com/flutter/flutter/pull/162454), MediaTek devices with API version less than 31, PowerVR devices older than CXT, and older versions of Samsung’s XClipse GPUs that don’t support Vulkan 1.3. + +As in prior releases, you can opt-out of Impeller by following [these instructions](https://docs.flutter.dev/perf/impeller#android). Due to our increasing confidence in Impeller’s stability and performance, the ability to opt-out of Impeller on newer Android devices will be removed in an upcoming stable release. + +Finally, Flutter 3.27 has a number of rendering bugs and crashes related to Impeller rendering on Vulkan capable devices that are fixed in 3.29 and later versions. We do not plan to hotfix any of these changes into 3.27 and we highly recommend updating to 3.29 or later. If your application is unable to do so, we recommend opting out of Impeller on Android devices. + +### Other Impeller updates + +This release improves Impeller’s text rendering. Notably, text animations are smoother with less jitter thanks to higher resolution glyphs in Impeller’s glyph atlas, and fixing rounding errors in floating point calculations. More details can be found in [flutter#149652](https://github.com/flutter/flutter/issues/149652). + +<DashImage figure src="images/0yGjm9dQU4oCG5XwG.webp" alt="Before" caption="Before" /> + + +<DashImage figure src="images/0s1EWYhGX9j4qOWB1.webp" alt="After" caption="After" /> + + +This release also includes a variety of other fidelity and performance improvements, including: + +* Conic curves are no longer approximated but [tessellated directly](https://github.com/flutter/flutter/pull/166165) + +* Partial repaint has been [optimized](https://github.com/flutter/flutter/pull/161626) to avoid frequent memory allocation + +* Blur speed has been improved by [removing redundant attachments](https://github.com/flutter/flutter/pull/165137) + +* [Fixed orientation](https://github.com/flutter/flutter/issues/164958) of text rotated exactly 180 degrees. + +## DevTools and IDEs + +### **[New] Flutter Property Editor** + +Easily edit widget properties and read documentation from the new Property Editor tool, which is accessible from the Flutter Property Editor sidebar panel ([VS Code](https://docs.flutter.dev/tools/vs-code#property-editor)) or tool window ([Android Studio / IntelliJ](https://docs.flutter.dev/tools/android-studio#property-editor)). + +<DashImage figure src="images/0ZbSuN_xSc7pXkUyC.webp" alt="VSCode" caption="VSCode" /> + + +<DashImage figure src="images/0KoBkOexQ6DyQdnQt.webp" alt="Android Studio & IntelliJ" caption="Android Studio & IntelliJ" /> + + +### **DevTools improvements** + +Additional improvements have been made across DevTools, including new offline support for the Network screen, bug fixes for issues related to review history, inspector errors, the Deep Links tool, and data improvements for the CPU Profiler and Memory screens. This release also includes several performance and memory improvements for DevTools, which will result in faster data load times and fewer memory-related crashes. + +These are just a few highlights from this release. To learn more about all the updates included in Flutter 3.32, check out the release notes for DevTools [2.44.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.44.0) and [2.45.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.45.0). + +### **Analyzer improvements** + +We have continued to make improvements to the Dart Analyzer, and hence the developer experience. This includes adding “doc imports,” a new comment-based syntax that enables external elements to be referenced in documentation comments without actually importing them. Check out the details on [dart.dev](https://dart.dev/tools/doc-comments/references#doc-imports). We’ve also made several quick fix, assist, and renaming improvements. + +### **Gemini in Android Studio now speaks fluent Flutter and Dart** + +Gemini in Android Studio now offers first-class support for Dart and Flutter development! This means you can leverage the power of Gemini directly within your favorite IDE to build beautiful, high-performance Flutter apps faster and easier than ever before. + +[Read this blog post](https://medium.com/flutter/gemini-in-android-studio-now-speaks-fluent-flutter-915dfec98274) to learn more. + +<DashImage figure src="images/151UZbL3Qb7BGDEuujTTg6A.gif" /> + + +### **Dart and Flutter support for the Model Context Protocol ([MCP](https://modelcontextprotocol.io/introduction)) is on the way** + +Support for the Model Context Protocol ([MCP](https://modelcontextprotocol.io/introduction)) and the recently published [Dart MCP SDK](https://pub.dev/packages/dart_mcp), is actively underway. A new [Dart Tooling MCP Server](https://github.com/dart-lang/ai/tree/main/pkgs/dart_tooling_mcp_server), also under development, will expose Dart and Flutter static, runtime, and ecosystem tooling to MCP clients, such as IDEs. + +This will drive the following benefits for Dart and Flutter developers: + +* More accurate and relevant code generation. + +* Complex tasks — like fixing layout issues, managing dependencies, or even resolving runtime errors — becomes feasible as the MCP protocol exposes semantic information from the *actual Dart and Flutter tools*. + +Watch this space! + +## Build with AI + +### **Introducing Firebase AI Logic** + +You might have heard of, or even used Vertex AI in Firebase, our client SDK for directly integrating Gemini API-driven functionality into your Flutter app. + +Many of you asked to use the Gemini Developer API, instead of Vertex AI, to take advantage of the generous free tier and to start adding generative AI at no cost. We heard you! From today, we are evolving Vertex AI in Firebase to Firebase AI Logic that provides access to both Gemini API providers with just one Flutter SDK. It enables you to use Gemini and Imagen models directly from your Flutter app rather than needing a server-side SDK. + +Check out the [`firebase_ai`](https://pub.dev/packages/firebase_ai) package for the new functionality. If you’re already using the [`firebase_vertexai`](https://pub.dev/packages/firebase_vertexai) package, it will continue to work as-is for now, but we encourage you to migrate to the new package. + +<DashImage figure src="images/0HlInLfTOQSZ3n-NG.webp" /> + + +### **Gain Actionable Insights with AI Monitoring dashboard** + +The new AI Monitoring dashboard in the Firebase console provides detailed, actionable insights into your Gemini API usage. This includes comprehensive visibility into consumption patterns, performance metrics, and potential issues. This enables data-driven decisions and ensures that you’re using the Gemini APIs in the most efficient way. AI Monitoring also helps you to debug your Gemini API usage, and identify and address any unexpected behaviors. + +<DashImage figure src="images/0d7zwnDUZqn9bgnoS.webp" /> + + +## Breaking changes and deprecations + +### Android accessibility announcements + +On Android, semantic announcement events are now [deprecated, as of API 36](https://api.flutter.dev/flutter/semantics/AnnounceSemanticsEvent-class.html#android). Instead, use “polite” implicit announcements by configuring a [`SemanticProperties.liveRegion`](https://api.flutter.dev/flutter/semantics/SemanticsProperties/liveRegion.html). Currently, there is a known limitation when announcing text that should not be focusable. See issue [#165857](https://github.com/flutter/flutter/issues/165857#issue-2945314471) for details on this limitation and the plans to resolve this issue. To see alternative solutions, check the [documentation on SemanticService.announce](https://api.flutter.dev/flutter/semantics/SemanticsService/announce.html). + +### **Discontinued support for 6 packages** + +As we planned and shared in our [Flutter 3.29 release blog post](https://medium.com/flutter/whats-new-in-flutter-3-29-f90c380c2317) we have discontinued support for the following packages: + +* `flutter_markdown`; see [flutter_markdown planned to be discontinued #162966](https://github.com/flutter/flutter/issues/162966) + +* `ios_platform_images`; see [ios_platform_images planned to be discontinued #162961](https://github.com/flutter/flutter/issues/162961) + +* `css_colors`; see [css_colors planned to be discontinued #162962](https://github.com/flutter/flutter/issues/162962) + +* `palette_generator`; see [palette_generator planned to be discontinued #162963](https://github.com/flutter/flutter/issues/162963) + +* `flutter_image`; see [flutter_image discontinued #162964](https://github.com/flutter/flutter/issues/162964) + +* `flutter_adaptive_scaffold`; see [flutter_adaptive_scaffold planned to be discontinued #162965](https://github.com/flutter/flutter/issues/162965) + +We encourage members of the community to refer to the discussion in each of the issues for alternatives. + +### iOS and macOS minimum versions + +Flutter will deprecate support for iOS 12 and macOS 10.14 (Mojave) in the next stable release, and will target a minimum of iOS 13 and macOS 10.15 (Catalina). This means that apps built against future versions of Flutter won’t run on iOS 12 or macOS 10.14. + +### Other breaking changes + +Other breaking changes and deprecations in this release include: + +* Deprecated the `ExpansionTileController` in Material in favor of the new reusable [`ExpansibleController`](https://main-api.flutter.dev/flutter/widgets/ExpansibleController-class.html) in the Widgets layer. + +* Renamed `SelectionChangedCause.scribble` (which is deprecated), to [`SelectionChangedCause.stylusHandwriting`](https://main-api.flutter.dev/flutter/services/SelectionChangedCause.html#stylusHandwriting), as Apple’s Scribble feature is now unified with Android’s Scribe. + +* As part of our continuing work to normalize Material theming, `ThemeData.indicatorColor` is deprecated in favor of [`TabBarThemeData.indicatorColor`](https://main-api.flutter.dev/flutter/material/TabBarThemeData/indicatorColor.html), while the component theme types for `cardTheme`, `dialogTheme`, and `tabBarTheme` will need to be migrated to [`CardThemeData`](https://main-api.flutter.dev/flutter/material/CardThemeData-class.html), [`DialogThemeData`](https://main-api.flutter.dev/flutter/material/DialogThemeData-class.html), and [`TabBarThemeData`](https://main-api.flutter.dev/flutter/material/TabBarThemeData-class.html), respectively. + +* The formula for `SpringDescription` in some behaviors has been corrected. This change fixes the problem where the behavior for some parameter combinations would not match expected real-world physics and exhibit discontinuity near the critical damping point. This breakage affects underdamped springs (damping ratio less than 1) with mass values other than 1. Such springs created prior to this change might exhibit different bouncing behaviors after upgrading. To restore previous animation behavior, update your spring parameters according to the [Spring Description Underdamped breaking change guide](https://docs.flutter.dev/release/breaking-changes/spring-description-underdamped), where you can also find [a tool](https://jsfiddle.net/DKWings/6jgvbzps/30/) that calculates the parameter adjustments. + +To see migration guides for all deprecations or breaking changes in this release, see the [breaking changes](https://docs.flutter.dev/release/breaking-changes) page. Running `dart fix` after updating will automatically migrate some of these changes for you. + +## Conclusion + +Flutter 3.32 marks another step forward, powered by the incredible dedication of our community. We’re thrilled to bring you features like web hot reload and continued platform improvements. Your contributions are invaluable, and we can’t wait to see what you build. For a comprehensive overview, check out the detailed release notes and changelog. To get started, simply run `flutter upgrade` and dive into the latest version! \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-3-35/images/13K1rgoDtIBWbIFZPp1VCEQ.webp b/sites/www/content/blog/whats-new-in-flutter-3-35/images/13K1rgoDtIBWbIFZPp1VCEQ.webp new file mode 100644 index 0000000000..707f45787e Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-35/images/13K1rgoDtIBWbIFZPp1VCEQ.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-35/images/1HQ1LCGF02lv7OmBz_lJ4-Q.webp b/sites/www/content/blog/whats-new-in-flutter-3-35/images/1HQ1LCGF02lv7OmBz_lJ4-Q.webp new file mode 100644 index 0000000000..01e09ea5a6 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-35/images/1HQ1LCGF02lv7OmBz_lJ4-Q.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-35/images/1KLn6ye1qAU9vAI3zgIhLCg.webp b/sites/www/content/blog/whats-new-in-flutter-3-35/images/1KLn6ye1qAU9vAI3zgIhLCg.webp new file mode 100644 index 0000000000..aa6d67178d Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-35/images/1KLn6ye1qAU9vAI3zgIhLCg.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-35/images/1Npm3gOKVmU1hMq4ujCPxCg.webp b/sites/www/content/blog/whats-new-in-flutter-3-35/images/1Npm3gOKVmU1hMq4ujCPxCg.webp new file mode 100644 index 0000000000..b7d29dd65e Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-35/images/1Npm3gOKVmU1hMq4ujCPxCg.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-35/images/1lbPiKmVYKfvwC8v20DuGWA.webp b/sites/www/content/blog/whats-new-in-flutter-3-35/images/1lbPiKmVYKfvwC8v20DuGWA.webp new file mode 100644 index 0000000000..b7782f4e8c Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-35/images/1lbPiKmVYKfvwC8v20DuGWA.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-35/images/1v2ml1DWIPJRRnz8zEo8QaQ.webp b/sites/www/content/blog/whats-new-in-flutter-3-35/images/1v2ml1DWIPJRRnz8zEo8QaQ.webp new file mode 100644 index 0000000000..a0f8e0b540 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-35/images/1v2ml1DWIPJRRnz8zEo8QaQ.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-35/index.md b/sites/www/content/blog/whats-new-in-flutter-3-35/index.md new file mode 100644 index 0000000000..514c5262cd --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-3-35/index.md @@ -0,0 +1,353 @@ +--- +title: "What’s new in Flutter 3.35" +description: "Hot Reload, Widget Previews, and More" +publishDate: 2025-08-14 +author: itsjustkevin +image: images/1KLn6ye1qAU9vAI3zgIhLCg.webp +category: releases +layout: blog +--- + +<DashImage figure src="images/1KLn6ye1qAU9vAI3zgIhLCg.webp" /> + + +## What’s new in Flutter 3.35 + +### Hot Reload, Widget Previews, and More + +## Introduction + +Welcome to the quarterly release of Flutter 3.35! This update is all about boosting your productivity, and brings the stable release of stateful hot reload on the web and the experimental launch of Widget Previews! Through the efforts of our community, Flutter 3.35 includes 1108 commits from 168 unique contributors, with 39 of them joining us for the first time. Let’s explore what’s new! + +## Web + +### Stateful hot reload on the web is now enabled by default + +We’re thrilled to announce this milestone for Flutter on the web and welcome everyone to add hot reload into their web development workflow. + +The positive feedback and issue reports we received during the experimental phase helped a lot. The Dart and Flutter web teams have been hard at work to improve the performance of hot-reloaded code while ensuring that more code changes can be successfully hot reloaded. + +Our goal is to provide a seamless and consistent hot reload experience across all platforms. While you can still disable this feature using a flag, we plan to remove that ability in a future release. + +### Wasm dry runs + +In anticipation of enabling WebAssembly (Wasm) as the default web build target, every JS build now performs a “dry run” compilation to Wasm. A series of checks determines the Wasm-readiness of your application, and any findings are emitted to the console as warnings. This feature can be toggled with the `--(no-)wasm-dry-run` flag. + +### What’s new? + +Previously, you had to explicitly enable stateful hot reload on the web using an experimental flag. Now, when you run your Flutter web application, stateful hot reload is available out of the box! Whether you run your Flutter web application from the command line or launch it from your IDE or coding agent, just run it in development mode and hot reload freely. + +The `--web-experimental-hot-reload` flag is no longer necessary to enable hot reload and you can remove it from your workflow or any IDE launch configurations you may have configured. + +### **Feedback** + +We know everyone can have a slightly different development environment, so if yours is broken by this change, you can disable hot reload with the flag `--no-web-experimental-hot-reload` for now. If you discover any issues, please file a bug using Dart’s [web hot reload issue template](https://github.com/dart-lang/sdk/issues/new?template=5_web_hot_reload.yml). For more information, see the [hot reload on the web documentation](https://docs.flutter.dev/platform-integration/web/building#hot-reload-web). + +### **What’s next?** + +Current active work will bring hot reload to more development environments, including `-d web-server`. We expect this will unlock more of your productivity while developing on the web, so keep an eye out for future releases! + +## Framework + +### A more inclusive and accessible experience + +Making applications accessible to all users is a cornerstone of the Flutter framework. This release continues our commitment with a host of improvements that give developers more control and refine the out-of-the-box experience for users of assistive technologies. + +**Richer semantics on the web** + +We’ve made significant strides in how Flutter web apps communicate with screen readers and other tools. For international users, we’ve added support for semantics locales ([#171196](https://github.com/flutter/flutter/pull/171196)), ensuring that accessibility features are presented in the user’s preferred language. + +**New tools for building accessible widgets** + +Developers now have more powerful tools to create sophisticated accessible experiences. The new `SemanticsLabelBuilder` widget ([#171683](https://github.com/flutter/flutter/pull/171683)) simplifies the process of combining multiple data points into a single, coherent announcement without messy string concatenation. + +For complex scrollable views, the new `SliverEnsureSemantics` widget ([#166889](https://github.com/flutter/flutter/pull/166889)) can be used to wrap slivers, ensuring they are always represented in the semantics tree, even when scrolled out of view. + +**Core widget and platform polish** + +This release is packed with fixes that polish the accessibility of core widgets. + +* On iOS, the `CupertinoSliverNavigationBar` now correctly respects accessible text scaling ([#168866](https://github.com/flutter/flutter/pull/168866)), and VoiceOver tab activation behavior now works correctly ([#170076](https://github.com/flutter/flutter/pull/170076)). + +* For Android, Talkback issues now work when using platform views ([#168939](https://github.com/flutter/flutter/pull/168939)), a critical fix for apps that embed native components. + +* Semantics properties for `CustomPainter` ([#168113](https://github.com/flutter/flutter/pull/168113)) are now backfilled, empowering you to make your custom-drawn UI fully accessible. + +* The text selection toolbar is now correctly aligned for right-to-left (RTL) languages, improving the experience for a global audience ([#169854](https://github.com/flutter/flutter/pull/169854)). + +### Material and Cupertino: More power and polish + +The Material and Cupertino libraries continue to evolve, giving you more control over your app’s look and feel. + +**New and enhanced components** + +We’ve added brand new, highly requested components and shipped significant enhancements to existing ones. + +* **Introducing DropdownMenuFormField ([#163721](https://github.com/flutter/flutter/pull/163721)):** You can now easily integrate the M3 `DropdownMenu` directly into your forms. + +* **Scrollable NavigationRail ([#169421](https://github.com/flutter/flutter/pull/169421)):** The `NavigationRail` can now be configured to scroll for cases where you have more destinations than can fit on the screen. + +* **NavigationDrawer header and footer ([#168005](https://github.com/flutter/flutter/pull/168005)):** You can now add a header and footer to the `NavigationDrawer`, providing more layout flexibility. + +* **Introducing CupertinoExpansionTile ([#165606](https://github.com/flutter/flutter/pull/165606)):** Create expandable and collapsible list items with the new `CupertinoExpansionTile` widget. + +<DashImage figure src="images/1Npm3gOKVmU1hMq4ujCPxCg.webp" /> + + +**Higher fidelity and interactivity** + +A key theme for this release was polishing our widgets to be as pixel-perfect as we can make them and behaviorally cohesive as their native counterparts. + +* Many Cupertino widgets have been updated to use the `RSuperellipse` shape ([#167784](https://github.com/flutter/flutter/pull/167784)), giving them the signature continuous-corner look that iOS users expect. + +* To make apps feel more alive and native, we’ve added haptic feedback to key interactive components like the `CupertinoPicker` ([#170641](https://github.com/flutter/flutter/pull/170641)) and `CupertinoSlider` ([#167362](https://github.com/flutter/flutter/pull/167362)). + +* The `Slider`’s value indicator can now be configured to be always visible ([#162223](https://github.com/flutter/flutter/pull/162223)). + +### More from the framework + +This release includes a number of powerful new features and refinements for some of the framework’s most essential parts, giving you more granular control over complex UI. + +**More powerful slivers** + +For developers building sophisticated scrolling experiences, you can now explicitly control the **paint order (or z-order) of slivers** ([#164818](https://github.com/flutter/flutter/pull/164818)), making it possible to correctly implement advanced effects like “sticky” headers that overlap other slivers without visual glitches. + +**Finer-grained control over navigation and forms** + +We’ve added several new features to give you more control over your app’s navigation and routing. + +* **Fullscreen dialogs ([#167794](https://github.com/flutter/flutter/pull/167794)):** The property `fullscreenDialog` was added to ModalRoute (plus all its descendants) and to `showDialog`. This enables customizing navigation behavior to and from dialog routes. + +* **Form Improvements ([#167060](https://github.com/flutter/flutter/pull/167060)):** `FormField` now includes an onReset callback, making it easier to handle form clearing logic. + +**Multi-window support (engine)** + +Our friends at Canonical continue to make excellent progress on adding support for multi-window applications! In this release, they landed the foundational logic to create and update windows in Windows and macOS ([#168728](https://github.com/flutter/flutter/pull/168728)). Subsequent releases will update Linux and introduce experimental APIs to expose multi-window functionality. Stay tuned! + +**Text input and selection refinements** + +Text input is a fundamental part of the user experience, and this release brings several refinements to make it more powerful and predictable. + +* **A more unified gesture system:** The introduction of the `PositionedGestureDetails` interface ([#160714](https://github.com/flutter/flutter/pull/160714)) unifies the details for all pointer-based gestures and allows you to write more generic gesture-handling code. + +* **iOS single-line scrolling ([#162841](https://github.com/flutter/flutter/pull/162841)):** To better align with native iOS behavior, single-line text fields are no longer scrollable by the user. + +**Android home/end key support ([#168184](https://github.com/flutter/flutter/pull/168184)):** We’ve added support for the `Home` and `End` keyboard shortcuts on Android. + +### Looking forward: Decoupling Flutter’s design libraries + +As the Flutter ecosystem continues to grow and mature, so too must the core framework. A key part of this maturation is ensuring that all parts of Flutter can evolve at the pace that best serves the community. + +To that end, we are beginning the process of moving the Material and Cupertino libraries out of the core Flutter framework and into their own standalone packages. We are just getting started on this work and, as always, are doing so openly and in collaboration with our contributor community. + +**Why is this changing?** + +This evolution will allow the Material and Cupertino libraries to innovate and release updates more frequently, independent of the quarterly Flutter release cycle. It also empowers the community to contribute more directly to the UI libraries they use every day. For the core framework, this change will lead to a more focused and stable foundation. + +**What does this mean for you?** + +There are no immediate changes to your workflow. This is the start of a long-term project, and we are committed to making the eventual transition as smooth as possible. We invite you to follow our progress, learn more about the plan, and share your feedback. + +* To read the detailed vision and rationale, see: **[Decoupling Design in Flutter](https://docs.google.com/document/d/189AbzVGpxhQczTcdfJd13o_EL36t-M5jOEt1hgBIh7w/edit?usp=sharing).** + +* To provide feedback and help coordinate the work, please [join the discussion on GitHub](https://github.com/flutter/flutter/issues/101479). + +This is a natural evolution for a mature and thriving ecosystem. We believe this change will lead to stronger and more agile Material and Cupertino libraries, with an even greater opportunity for community collaboration. + +## iOS + +### iOS 26 and Xcode 26 beta compatibility + +We are actively working on Flutter’s compatibility with the [iOS 26 betas](https://www.apple.com/os/ios/). Please help us by testing your apps and plugins on the latest beta releases and [filing any new issues](https://github.com/flutter/flutter/issues/new/choose) you discover. You can track our progress and see a list of known issues in this [GitHub project](https://github.com/orgs/flutter/projects/212). + +## Android + +### Sensitive content protection + +On Android, you can now protect sensitive user content during media projection by using the `SensitiveContent` widget in your Flutter app. With API 35 and later, you can use this widget to obscure the entire screen during a screen share and help prevent data theft. For more details on this feature and how to use it, check out [Protect sensitive content](https://docs.flutter.dev/platform-integration/android/sensitive-content). + +## Engine + +### General engine updates + +3.35 continues to focus on improving Impeller performance and compatibility. Investments have also been made to adopt `UISceneDelegate` for iOS and to introduce additional tooling improvements. Some notable additions are below. + +**Engine** + +* Optimization: App startup time has been reduced by performing initialization on a new thread. + +**Impeller** + +* Optimization: Removed MSAA from intermediate targets. + +* Fidelity: Removed lossy texture compression default. + +* Fix: Fixed stuck `MaskFilter` in subsequent draws. + +* Optimization: Path rendering. + +* New: Added Vivante GPU support. + +* Optimization: `DrawImageNine` now use fast Porter Duff blends. + +* Fix: Fixed memory leak in VideoPlayer + +* Fidelity: Increased blur radius calculation for cleaner blurs with large sigmas. + +**iOS** + +* Fix: Resolved crash in `WebView` wrapped with `ClipRSuperellipse`. + +* Fix: Image decoding now works when launching apps in the background from a remote notification. + +* Revival: Brought back Live Text (OCR) option to text fields. + +**Android** + +* Migration: template project migrated to Android 24. + +* Fix: Fixed crash on Android <= 14 from background image readers. + +* Fix: Fixed uniform arrays in fragment shaders for OpenGLES Impeller. + +* Fix: Fixed upside down fragment shader passes in OpenGLES Impeller. + +* Fix: Fixed crash in `FlutterEngineGroup`. + +**macOS** + +* Fix: Fixed Display P3 colors. + +## DevTools and IDEs + +### The Dart and Flutter MCP Server is now in the stable channel + +The experimental [Dart and Flutter MCP Server](https://dart.dev/tools/mcp-server) is now available on the stable channel of the Dart SDK. This marks a major step forward in our vision for a highly productive, AI-assisted development experience for every Dart and Flutter developer. + +**Enhanced Dart and Flutter context for AI coding assistants** + +The Dart and Flutter MCP Server acts as a bridge, giving AI coding assistants access to even more of your project’s context via the Dart and Flutter toolchain. Instead of just suggesting code, your AI assistant can now understand your project deeply and take action on your behalf. This allows you to stay focused on your goals while the AI handles the mechanics. + +With the Dart and Flutter MCP Server, you can ask your AI assistant to: + +* **Fix runtime errors**: Inspect the live widget tree, identify a Flutter RenderFlex overflow, and apply the correct fix automatically. + +* **Manage dependencies**: Find the best package on pub.dev for a specific task, add it to your `pubspec.yaml`, and run `pub get`. + +* **Write and correct code**: Generate the boilerplate for a new feature, and then self-correct any analysis errors it introduces along the way. + +* And more…. + +**Connect to your favorite AI tools** + +By connecting your favorite AI tools like Gemini Code Assist, Firebase Studio, Gemini CLI, GitHub Copilot, and Cursor to the Dart and Flutter MCP Server, you can unlock a new level of productivity. + +For a comprehensive guide on what the Dart and Flutter MCP Server can do and how to configure it in your editor of choice, check out our detailed blog post [Supercharge Your Dart & Flutter Development Experience with the Dart and Flutter MCP Server](https://medium.com/flutter/supercharge-your-dart-flutter-development-experience-with-the-dart-mcp-server-2edcc8107b49), and [the documentation](https://dart.dev/tools/mcp-server). + +<DashImage figure src="images/13K1rgoDtIBWbIFZPp1VCEQ.webp" alt="*Fixing a layout issue, enabled by the Dart and Flutter MCP Server (Note: For brevity, parts of this recording have been sped up.)*" caption="*Fixing a layout issue, enabled by the Dart and Flutter MCP Server (Note: For brevity, parts of this recording have been sped up.)*" /> + + +### DevTools updates + +We’ve continued to make general, UX, and performance updates to DevTools. To learn more about all the updates included in Flutter 3.35, check out the release notes for DevTools [2.46.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.46.0), [2.47.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.47.0) and [2.48.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.48.0). + +### Experimental Widget Previews are here! + +A highly-requested feature from the community is the ability to preview widgets in isolation, similar to tools available in other modern UI toolkits. We’ve heard you, and today, we’re thrilled to take the first step in addressing this with an early experimental version of **Flutter Widget Previews**, now available in the stable channel! + +Flutter’s stateful hot reload is already best-in-class for iterating quickly on a running application. Widget previews complements this by allowing you to visualize and test your widgets in a sandboxed environment, completely separate from a full app. This is invaluable when building out a design system or testing a component across a matrix of different configurations, such as various screen sizes, themes, and text scales, all at once and side-by-side. + +<DashImage figure src="images/1lbPiKmVYKfvwC8v20DuGWA.webp" alt="*Experimenting with theming in Widget Previews*" caption="*Experimenting with theming in Widget Previews*" /> + + +This is an early, experimental release, and your feedback is critical in shaping its future. The APIs and user experience are not yet stable and will change as we learn from you. Based on early feedback, we are already planning future enhancements, including: + +1. **Tighter IDE integration**: We plan to move beyond the current separate browser window to offer an optional, embedded preview pane directly within VS Code, Android Studio and IntelliJ IDEA. + +1. **Generic theming support**: As we move forward with our plan mentioned earlier in this blogpost to decouple the Material and Cupertino design libraries from the core framework, Widget Previews will evolve to support custom and generic design systems more effectively. + +We are incredibly excited to get this into your hands. To get started, check out the documentation and let us know what you think! + +* **Read the docs**: **[**Getting Started with Flutter Widget Previewer (Experimental)](https://docs.flutter.dev/development/tools/widget-previewer) + +* **Give feedback**: File issues and feature requests in the[ Flutter GitHub repository](https://github.com/flutter/flutter/issues/new/choose). + +* **Learn more**: For a technical deep-dive, see the[ Flutter Widget Previews Architecture document](https://flutter.dev/go/widget-previews-architecture). + +### Analysis Server speed improvements + +The [dart command-line tool](https://dart.dev/tools/dart-tool) commands that use the analysis server now run the AOT-compiled analysis server snapshot. These include `dart analyze`, `dart fix`, and `dart language-server`. + +There is no functional difference when using the AOT-compiled analysis server snapshot, but various tests indicate that there is a significant speedup in the time to analyze a project. Check out the change in stats when we ran some common commands on a sample package after this update: + +<DashImage figure src="images/1HQ1LCGF02lv7OmBz_lJ4-Q.webp" /> + + +Results vary depending on the source code, but in general, some short commands like formatting now complete in a fraction of the time and longer running ones like analysis got nearly 50% faster. This should make your everyday use of the tool feel much more responsive, and could make a big difference in common test workflows running in CI systems. + +### Android Studio and Jetbrains IDE plugin updates + +With the release of the [Flutter IntelliJ Plugin](https://plugins.jetbrains.com/plugin/9212-flutter) version M87, there is now support for Android Studio [Meerkat](https://developer.android.com/studio/archive) and [Narwhal](https://developer.android.com/studio/preview). With this version, the Flutter plugin can now additionally be installed on CLion, GoLand, PyCharm, and several other JetBrains IDEs! For the full list, see the supported products [here](https://plugins.jetbrains.com/plugin/9212-flutter/versions/stable/818757). Thank you to Alex Li ([Flutter and Dart GDE](https://developers.google.com/profile/u/AlexV525)) whose [PR #7949](https://github.com/flutter/flutter-intellij/pull/7949) and diligent collaboration with our team was a driving force to expand the number of Jetbrains IDEs supported by the plugin. + +For a list of all the updates and fixes, see our [changelog](https://github.com/flutter/flutter-intellij/blob/master/CHANGELOG.md) and file issues [here](https://github.com/flutter/flutter-intellij/issues). To get the latest update: **Settings > Plugins > Flutter > Update**. + +Consider subscribing to the dev channel to test the latest updates as they are released weekly. Find instructions are [here](https://github.com/flutter/flutter-intellij/blob/master/docs/Dev-Channel.md). + +<DashImage figure src="images/1v2ml1DWIPJRRnz8zEo8QaQ.webp" alt="*The full set of IDEs supported by the Flutter IntelliJ Plugin version M87.*" caption="*The full set of IDEs supported by the Flutter IntelliJ Plugin version M87.*" /> + + +## Breaking changes and deprecations + +This release includes several important deprecations and breaking changes that are part of our ongoing effort to modernize and improve the Flutter framework. + +* **Component theme normalization:** This release marks the completion of a significant, long-term project to normalize component themes. To make theming more consistent and align with Material 3, component themes like `AppBarTheme`, `BottomAppBarTheme`, and `InputDecorationTheme` have been refactored to be based on new, data-oriented `…ThemeData` classes. + +* **Radio widget redesign:** The `Radio`, `CupertinoRadio`, and `RadioListTile` widgets have been redesigned to improve accessibility. The `groupValue` and `onChanged` properties have been deprecated in favor of a new `RadioGroup` widget that manages the state for a set of radio buttons. + +* **Form widget and slivers:** The `Form` widget can no longer be used directly as a sliver. To include a `Form` in a `CustomScrollView`, it should be wrapped in a `SliverToBoxAdapter`. + +* **Semantics elevation and thickness removal:** The `elevation` and `thickness` properties on `SemanticsConfiguration` and `SemanticsNode` have been removed. These properties were unimplemented and their removal simplifies the API. + +* **DropdownButtonFormField value deprecation:** The `value` parameter on `DropdownButtonFormField` has been deprecated and renamed to `initialValue` to more accurately reflect its behavior. + +For more details and migration guidance, see our [breaking changes page](https://docs.flutter.dev/release/breaking-changes). + +**Deprecated 32-bit x86 architecture on Android** + +Flutter has deprecated the 32-bit x86 architecture on Android. For most developers this will have no impact, as it mainly affects older x86-based Android emulators and a small number of physical devices. 32-bit ARM and 64-bit x86_64 emulators and devices are still supported. The full technical breakdown is available on the [Dart GitHub deprecation issue](https://github.com/dart-lang/sdk/issues/49969). + +**Deprecated IDE support for older Flutter SDKs** + +With this release, we are deprecating IDE support for Flutter SDKs before 3.13. + +In our next stable release, Flutter SDKs before 3.16 will be deprecated. + +To learn more about Flutter’s deprecation policy, visit the [Flutter compatibility](https://docs.flutter.dev/release/compatibility-policy#deprecation-policy) page. To learn more about the deprecation policy regarding IDEs, visit [SDK support for Flutter developer tools](https://docs.flutter.dev/tools/sdk#sdk-support-for-flutter-developer-tools). + +If you are a user of an older Dart or Flutter SDK that is no longer supported, please be sure to [download a compatible version](https://docs.flutter.dev/install/upgrade). + +**Minimum Android SDK has changed** + +The minimum Android SDK supported by Flutter, provided by `flutter.minSdkVersion`, is now API 24 (Android 7). If you have modified this value to something other than `flutter.minSdkVersion` you need to bump it to at least 24. + +Also, Flutter’s minimum required values for some Android specific dependencies has increased. The Flutter tool will inform you if you need to increase any of these values, as well as provide guidance on how to upgrade (if the tool doesn’t print any warnings, no action is required). + +You must now at least have: + +* Gradle version: 8.7.0 + +* Android Gradle Plugin (AGP): 8.6.0 + +* Java: 17 + +To address AGP and Gradle issues, either [run the AGP Upgrade Assistant in Android Studio](https://developer.android.com/build/agp-upgrade-assistant) or [run it from the command line](https://docs.flutter.dev/release/breaking-changes/android-java-gradle-migration-guide#solution-2-command-line). + +**Deprecation of pluginClass: none** + +Support for `pluginClass: none` in plugin `pubspec.yaml` files will be removed in future versions of Flutter, and any plugin using this field should remove it to ensure forward compatibility. All recent versions of Flutter support having no `pluginClass:` entry when there is no native code for that platform. + +## Conclusion + +Flutter 3.35 is the result of a collective effort to build a better developer experience for everyone. We’re grateful for the contributions and feedback from our community including teams such as Canonical, that helped make features like web hot reload and Widget Previews a reality. Your passion is what drives Flutter forward, and we’re excited to see the amazing things you’ll create with these new tools. + +For a complete list of all the changes, be sure to check out the detailed release notes and changelog. To get started, with everything in Flutter 3.35, run `flutter upgrade.` We can’t wait to see what you build! \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-3-38/images/14Yk43MDOrzRUt3lfkVKY0g.webp b/sites/www/content/blog/whats-new-in-flutter-3-38/images/14Yk43MDOrzRUt3lfkVKY0g.webp new file mode 100644 index 0000000000..84b289bd9b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-38/images/14Yk43MDOrzRUt3lfkVKY0g.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-38/images/1Hvcea8STYP87B_MK01-buA.webp b/sites/www/content/blog/whats-new-in-flutter-3-38/images/1Hvcea8STYP87B_MK01-buA.webp new file mode 100644 index 0000000000..b474bb82eb Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-38/images/1Hvcea8STYP87B_MK01-buA.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-38/images/1MMtTv_yj5ycI6is3vkXRow.webp b/sites/www/content/blog/whats-new-in-flutter-3-38/images/1MMtTv_yj5ycI6is3vkXRow.webp new file mode 100644 index 0000000000..354471f55e Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-38/images/1MMtTv_yj5ycI6is3vkXRow.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-38/images/1NE0-VXAW-sVf3VESZEfZrw.webp b/sites/www/content/blog/whats-new-in-flutter-3-38/images/1NE0-VXAW-sVf3VESZEfZrw.webp new file mode 100644 index 0000000000..c94822eeda Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-38/images/1NE0-VXAW-sVf3VESZEfZrw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-38/images/1SuEDY7F5DX6Ar2HQB523EQ.webp b/sites/www/content/blog/whats-new-in-flutter-3-38/images/1SuEDY7F5DX6Ar2HQB523EQ.webp new file mode 100644 index 0000000000..5764a567d5 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-38/images/1SuEDY7F5DX6Ar2HQB523EQ.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-38/images/1cvsiQjlzqc54_6D4HCsZCQ.gif b/sites/www/content/blog/whats-new-in-flutter-3-38/images/1cvsiQjlzqc54_6D4HCsZCQ.gif new file mode 100644 index 0000000000..6d525212c5 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-38/images/1cvsiQjlzqc54_6D4HCsZCQ.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-38/images/1eYq9jc4L08SjK4DYZx1qZQ.webp b/sites/www/content/blog/whats-new-in-flutter-3-38/images/1eYq9jc4L08SjK4DYZx1qZQ.webp new file mode 100644 index 0000000000..fc5d61a05b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-38/images/1eYq9jc4L08SjK4DYZx1qZQ.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-38/images/1ecwWHV6xbxpgEhgCf11kug.webp b/sites/www/content/blog/whats-new-in-flutter-3-38/images/1ecwWHV6xbxpgEhgCf11kug.webp new file mode 100644 index 0000000000..0d3d28f4ba Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-38/images/1ecwWHV6xbxpgEhgCf11kug.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-38/index.md b/sites/www/content/blog/whats-new-in-flutter-3-38/index.md new file mode 100644 index 0000000000..e7f4d9255c --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-3-38/index.md @@ -0,0 +1,344 @@ +--- +title: "What’s new in Flutter 3.38" +description: "Write less, see more, build faster" +publishDate: 2025-11-12 +author: itsjustkevin +image: images/1MMtTv_yj5ycI6is3vkXRow.webp +category: releases +layout: blog +--- + +<DashImage figure src="images/1MMtTv_yj5ycI6is3vkXRow.webp" /> + + +## What’s new in Flutter 3.38 + +### Write less, see more, build faster + +## Introduction + +Welcome back to our regularly schedule quarterly release, Flutter 3.38. This update is all about boosting your productivity and refining the developer experience with dot shorthands and updates to Widget Previews. Thanks to our community, this release includes 825 total commits from 145 unique contributors, with 37 being first time contributors. Let’s dig into what’s in this release. + +## Dot shorthands + +Write more concise Dart code! We’re excited to announce a new Dart feature — **dot shorthands**! Shorthands reduces boilerplate by letting you omit types that Dart can infer. + +For example, you can use shorthands to write `.start` instead of `MainAxisAlignment.start` . + +```less +// With shorthands +Column( + mainAxisAlignment: .start, + crossAxisAlignment: .center, + children: [ /* ... */ ], +), + +// Without shorthands +Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ /* … */ ], +), +``` + + +This also works for named constructors! You can write `.all` instead of `EdgeInsets.all`: + +```css +Padding( + padding: .all(8.0), + child: Text('Hello world'), +), +``` + + +This feature is on by default in Dart 3.10 and Flutter 3.38. For more information, check out the [dot shorthands](https://dart.dev/language/dot-shorthands) page on dart.dev. You can also read about this and more (like Dart hooks!) in the [Dart 3.10 release blog](https://blog.dart.dev/ea8b952b6088) post. + +## Web + +### Web development configuration files + +The `flutter run` command now supports a configuration file for web settings. This enables you to specify host, port, certificate, and header information in a `web_dev_config.yaml` file at the root of your project. Check the file in so everyone on your team debugs with the same settings. For more information, visit [setting up a web development configuration file](https://docs.flutter.dev/platform-integration/web/web-dev-config-file). + +### Web development proxy settings + +Along with existing command line flags, the web dev config file also supports new proxy settings. Proxy settings make it possible to forward requests to configured paths to another server. This makes it easier to develop a web client that connects to dynamic endpoints on the same host. + +Details about proxy settings are also at [setting up a web development configuration file](https://docs.flutter.dev/platform-integration/web/web-dev-config-file). + +### Expanded support for hot reload on the web + +Stateful hot reload is now enabled by default when running with `-d web-server` and you open the link to your Flutter application in a browser. This even works with multiple browsers connected at the same time. + +As with `-d chrome`, this feature can be temporarily disabled using the `--no-web-experimental-hot-reload` flag. The ability to disable the feature will be removed in a future release so, if you encounter problems in your development workflow, please file a bug using Dart’s [web hot reload issue template](https://github.com/dart-lang/sdk/issues/new?template=5_web_hot_reload.yml). For more information, see the [hot reload on the web documentation](https://docs.flutter.dev/platform-integration/web/building#hot-reload-web). + +## Framework + +This release includes a number of powerful new capabilities and refinements across the framework, giving developers more granular control over advanced UI, navigation, and platform interactions. + +Developers now have more power when creating pop-ups, dialogs, and other floating UI elements with `OverlayPortal`. It is now possible to render a child in any `Overlay` up the widget tree using `OverlayPortal.overlayChildLayoutBuilder` ([#174239](https://github.com/flutter/flutter/pull/174239)), making it easier to show an app-wide notification or other UI that needs to escape the layout constraints of its parent widget. The underlying Overlay.of method was also made more robust and efficient ([#174315](https://github.com/flutter/flutter/pull/174315)). + +For a more modern Android navigation experience, predictive back route transitions are now enabled by default in `MaterialApp` ([#173860](https://github.com/flutter/flutter/pull/173860)). When a user performs the back gesture, they now see a preview of the home screen as the current route animates away. In addition, the default page transition has been updated to [`FadeForwardsPageTransitionsBuilder`](https://main-api.flutter.dev/flutter/material/FadeForwardsPageTransitionsBuilder-class.html) from [`ZoomPageTransitionsBuilder`](https://main-api.flutter.dev/flutter/material/ZoomPageTransitionsBuilder-class.html) to reflect native behavior. + +This release also deepens desktop integration. On Windows, developers can now access a list of connected displays and query detailed properties for each, such as resolution, refresh rate, and physical size ([#164460](https://github.com/flutter/flutter/pull/164460)). This enables the creation of applications with sophisticated window management features. + +Finally, the framework itself is now more resilient. Errors that occur in widget lifecycle callbacks (such as `didUpdateWidget`) are now handled more gracefully, preventing them from causing cascading failures in the element tree ([#173148](https://github.com/flutter/flutter/pull/173148)). `ResizeImage` now correctly implements equality, which makes image caching and comparison more predictable by ensuring that identical `ResizeImage` providers are treated the same ([#172643](https://github.com/flutter/flutter/pull/172643)). + +On the web, UI polish continues with a fix to `RSuperellipse` that prevents rendering errors when corner radii are larger than the widget itself ([#172254](https://github.com/flutter/flutter/pull/172254)), instead, such cases will be treated to produce the pill shape as expected. + +For international users, detecting the browser’s preferred locale is now more robust. The engine now uses the standard `Intl.Locale` web API to parse browser languages, replacing the previous manual and more fragile implementation ([#172964](https://github.com/flutter/flutter/pull/172964)). This change leads to more reliable locale detection and a better experience for a global audience. + +An Android-specific bug ([#171973](https://github.com/flutter/flutter/pull/171973)) has been resolved, primarily affecting Samsung devices with hardware keyboards. Previously, after a user interacted with a `TextField`, the Android Input Method Editor (IME) could get stuck in a stale state. This caused the IME to mistakenly intercept “Enter” or “Space” key presses, preventing non-text widgets like a `Checkbox` or `Radio` button from receiving the event. The fix ensures that the `InputMethodManager` is correctly reset when the text connection closes, clearing the IME’s stale state and restoring predictable hardware keyboard interaction for users. + +### Material and Cupertino updates + +The Material and Cupertino libraries continue to evolve with a focus on API consistency and polished user experiences. This release brings a major API migration, new widget capabilities, and numerous refinements that make building beautiful, functional UIs more straightforward. + +Building on the deprecation of `MaterialState`, this release continues the internal migration to the more unified `WidgetState`. This provides a consistent, expressive way to define a widget’s appearance across different interaction states, such as pressed, hovered, or disabled, and requires no change for existing apps. This migration has been applied to a wide range of widgets and their themes, including `IconButton`, `ElevatedButton`, `Checkbox`, and `Switch` ([#173893](https://github.com/flutter/flutter/pull/173893)). The new API also adds power and flexibility; for instance, `IconButton` now includes a `statesController` property ([#169821](https://github.com/flutter/flutter/pull/169821)) that allows for programmatic control over its visual states, opening the door to more custom and interactive designs. + +This release also introduces several new features and convenience APIs. The `Badge.count` constructor now includes a `maxCount` parameter ([#171054](https://github.com/flutter/flutter/pull/171054)) to easily cap the displayed count (for example, showing “99+” instead of “100”). + +<DashImage figure src="images/1ecwWHV6xbxpgEhgCf11kug.webp" /> + + +For finer-grained gesture control, the `InkWell` widget now features an `onLongPressUp` callback ([#173221](https://github.com/flutter/flutter/pull/173221)), useful for triggering actions that should only complete when the user lifts their finger. + +The Cupertino library also continues its journey toward better iOS fidelity. The `CupertinoSlidingSegmentedControl` adds an `isMomentary` property ([#164262](https://github.com/flutter/flutter/pull/164262)) to allow the control to trigger actions without persisting a selection. To better match native iOS behavior, the `CupertinoSheet` now features a subtle “stretch” effect when dragged upward while fully expanded ([#168547](https://github.com/flutter/flutter/pull/168547)). + +Finally, this release is packed with refinements that polish the behavior of core components. Highlights include a fix for `DropdownMenuFormField` to correctly clear its text field when a form is reset ([#174937](https://github.com/flutter/flutter/pull/174937)) and updates to `SegmentedButton` to improve focus handling ([#173953](https://github.com/flutter/flutter/pull/173953)) and ensure its border correctly reflects the widget’s state ([#172754](https://github.com/flutter/flutter/pull/172754)). + +### Decoupling Material and Cupertino + +We’ve been doing a lot of planning for decoupling the Material and Cupertino libraries from the framework. The following list includes several discussions around some recently published design docs. + +**Improving the release process for flutter/packages, which will include Material and Cupertino after decoupling.** + +* Status: Decided + +* Ideas: [First-Party Package Release Strategy](https://docs.google.com/document/u/1/d/18kjoP-4LAXEllugVOQRg6vZELyD6MuxlKilLD4lFxSY/edit) + +* Decided on: [Batch Release One Pager (PUBLICLY SHARED)](https://docs.google.com/document/d/1jUoFaawutbYsCI5oY3pDP_l-xpv6FhDKlcI1-EoT02s/edit?tab=t.0) + +**Colors and dot shorthands** + +* Status: Decided + +* [A Basic Color Set for Flutter (PUBLICLY SHARED)](https://docs.google.com/document/d/1y38TN9AUTyd0eTbu4kx4FiNgfsLDPvWvi92Fv5HWFjQ/edit?tab=t.0#heading=h.pub7jnop54q0) + +**Decoupling tests** + +* Status: In progress + +* [Decoupling Framework Tests (PUBLICLY SHARED)](https://docs.google.com/document/d/1UHxALQqCbmgjnM1RNV9xE2pK3IGyx-UktGX1D7hYCjs/edit?tab=t.0) + +* [https://github.com/flutter/flutter/issues/177028](https://github.com/flutter/flutter/issues/177028) + +**Text** + +* Status: In discussion + +* [Flutter Decoupling Design From Text (PUBLICLY SHARED)](https://docs.google.com/document/d/1oFezK5leJzTWA5lsw3BQGx7gLbhpSL8dMleU3HD7bNY/edit?tab=t.0) + +### Scrolling: more robust and predictable slivers + +This release brings a number of fixes that make building complex scrolling layouts, especially those using `SliverMainAxisGroup` and `SliverCrossAxisGroup`, more robust and predictable. + +Developers using these widgets to group multiple slivers will find that gesture handling is now more reliable. Hit-testing for taps and other pointer events on slivers within these groups is now correctly calculated, ensuring that user interactions behave as expected ([#174265](https://github.com/flutter/flutter/pull/174265)). + +Several other fixes contribute to more accurate scrolling behavior within a `SliverMainAxisGroup`. Over-scrolling issues when using a pinned header are resolved ([#173349](https://github.com/flutter/flutter/pull/173349)), calling `showOnScreen` to reveal a sliver now works correctly ([#171339](https://github.com/flutter/flutter/pull/171339)), and the internal scroll offset calculation is more precise ([#174369](https://github.com/flutter/flutter/pull/174369)). + +For developers building custom scroll views, the new `SliverGrid.list` constructor ([#173925](https://github.com/flutter/flutter/pull/173925)) offers a cleaner way to create a grid from a simple list of children. + +This release also improves focus navigation for keyboard and D-pad users in complex layouts. In nested scroll views with different scroll axes (such as a vertical list of horizontal carousels), directional focus navigation is now more predictable, preventing the focus from jumping unexpectedly between sections ([#172875](https://github.com/flutter/flutter/pull/172875)). + +### Accessibility: a more inclusive experience for all users + +Making applications accessible to all users is a cornerstone of the Flutter framework. This release continues that commitment by giving developers more programmatic control, improving the experience for international users, and polishing the accessibility of core widgets. + +For developers building complex applications, this release introduces the ability to turn on accessibility by default on iOS by using `WidgetsFlutterBinding.instance.ensureSemantics` ([#174163](https://github.com/flutter/flutter/pull/174163)). Debugging accessibility issues is now easier, as `debugDumpSemanticsTree` includes additional text input validation results information to help diagnose problems more quickly ([#174677](https://github.com/flutter/flutter/pull/174677)). + +For advanced accessibility in sliver-based scrolling views, the new `SliverSemantics` widget ([#167300](https://github.com/flutter/flutter/pull/167300)) is now available. Much like the existing `Semantics` widget, developers can use `SliverSemantics` inside a `CustomScrollView` to annotate portions of their sliver tree with specific semantic information. This is particularly useful for annotating headers, assigning semantics roles, and adding descriptive labels to slivers for screen readers, providing a more understandable and accessible experience for users. + +Finally, the accessibility of core widgets continues to be refined. The `CupertinoExpansionTile` is now accessible by default ([#174480](https://github.com/flutter/flutter/pull/174480)), and the `AutoComplete` widget now announces the status of search results to the user ([#173480](https://github.com/flutter/flutter/pull/173480)). Other improvements, such as larger touch targets in the `TimePicker` ([#170060](https://github.com/flutter/flutter/pull/170060)), contribute to a more accessible out-of-the-box experience. + +## iOS + +We are happy to confirm that Flutter fully supports the latest platform releases: iOS 26, Xcode 26, and macOS 26, all of which were released in September. This ensures you can immediately begin developing and testing your apps on Apple’s newest operating systems and tooling. + +You might have already noticed significant quality-of-life improvement for iOS developers shipped in the last release in Flutter, addressing a long-standing user annoyance: the requirement for the Xcode application to launch automatically when running Flutter apps on physical devices using `flutter run`. We’ve introduced a new deployment method using the Xcode 26 command line tool, `devicectl`, for app installation, launch, and debugging. This transition eliminates the need to invoke the Xcode application during deployment, and relies solely on command-line Xcode build tools in most cases. If you see problems, you can disable this deployment method with `flutter config --no-enable-lldb-debugging`, and please [file an issue](https://github.com/flutter/flutter/issues/new/choose) to let us know! + +Previously, this feature relied on Xcode automation which became unstable and flakey on Xcode 26, especially when running consecutive commands. If you are now developing for the newest Apple releases, we strongly recommend updating your version of Flutter to 3.38 or higher. + +### UIScene Lifecycle Migration + +Flutter 3.38 includes essential support for the Apple-mandated [UIScene lifecycle](https://developer.apple.com/documentation/technotes/tn3187-migrating-to-the-uikit-scene-based-life-cycle). This is a critical, proactive update following Apple’s announcement at WWDC25: “In the release following iOS 26, any UIKit app built with the latest SDK will be required to use the UIScene life cycle, otherwise it will not launch.” + +To ensure your iOS Flutter applications remain compatible and launch successfully on future iOS releases, a migration is required. + +**Migrating Flutter applications** + +All existing iOS Flutter applications must migrate to the new lifecycle. You have two paths to complete this migration: + +1. Manual migration: Follow the manual migration instructions provided on Flutter’s [website](https://docs.flutter.dev/release/breaking-changes/uiscenedelegate#migration-guide-for-flutter-apps). + +1. Automatic migration (Experimental): Enable an experimental feature to handle the migration automatically. This will be enabled by default in a future release. Run the following command: + +`flutter config --enable-uiscene-migration` + +**Migrating Flutter plugins** + +Flutter plugins that rely on application lifecycle events must be updated to use the UIScene lifecycle events. Plugin developers should refer to the [migration guide](https://docs.flutter.dev/release/breaking-changes/uiscenedelegate#migration-guide-for-flutter-plugins). Plugins that have not migrated will display warnings in a future release. + +**Migrating embedded Flutter (Optional)** + +For projects that embed Flutter within a native host application, migrating is optional but highly recommended. Adopting Flutter’s new UIScene APIs using the [add to app migration guide](https://docs.flutter.dev/release/breaking-changes/uiscenedelegate#migration-guide-for-adding-flutter-to-existing-app-add-to-app), enables scene lifecycle events for your plugins, ensuring compatibility with the Flutter ecosystem + +## Android + +### 16KB page size compatibility + +Upgrading to Flutter 3.38 is essential preparation for [Google Play’s 16 KB page size compatibility requirement](https://developer.android.com/guide/practices/page-sizes). Starting [November 1, 2025](https://android-developers.googleblog.com/2025/05/prepare-play-apps-for-devices-with-16kb-page-size.html), apps targeting Android 15 and higher must support 16 KB pages. This change ensures your app runs correctly on high-RAM devices, offering performance benefits like up to 30% faster launches. Flutter 3.38 updates the default Android ndkVersion to NDK r28, the minimum required for native code to achieve the proper alignment for 16 KB support. + +### Memory fixes + +Flutter 3.38 [fixes](https://github.com/flutter/flutter/issues/173770) a significant memory leak impacting all Flutter apps on Android. The issue (introduced in 3.29.0) occurred when Activities were destroyed upon exit, as configured by developer settings or when Activities were killed by the system due to low memory. + +### Android dependency updates + +It can often be a challenge to figure out the right combination of versions for Android dependencies that will work for your app, including Gradle, the Android Gradle Plugin (AGP), the Kotlin Gradle Plugin (KGP), Java, and others. For the Flutter 3.38 release, we tested and confirmed compatibility in our continuous integration (CI) environment with the following set of Android dependency versions: + +* **Java 17**: required minimum version for Android development in Flutter 3.38. + +* **KGP 2.2.20**: [maximum known and supported](https://kotlinlang.org/docs/gradle-configure-project.html#apply-the-plugin) Kotlin Gradle Plugin version for the tooling. + +* **AGP 8.11.1**: the newest Android Gradle Plugin version [compatible](https://kotlinlang.org/docs/gradle-configure-project.html#apply-the-plugin) with KGP 2.2.20. + +* **Gradle 8.14**: this version works with the chosen versions of Java, KGP, and AGP. Note that Gradle 8.13 is the minimum version required for AGP 8.11.1. + +To ensure that your application seamlessly across Flutter releases, we strongly encourage you to use the API level variables vended by the Flutter SDK in your build files. The configured values for this release are: + +* `flutter.compileSdkVersion` (API 36) + +* `flutter.targetSdkVersion` (API 36) + +* `flutter.minSdkVersion` (API 24) or higher + +## Engine + +### Performance overlay + +The performance overlay has been refactored to be more efficient, reducing its rendering time on both Skia and Impeller backends. This means you get more accurate performance data with less overhead. ([#176364](https://github.com/flutter/flutter/pull/176364)) + +### **Vulkan and OpenGL ES** + +Numerous fixes and improvements to the Vulkan and OpenGL ES backends improve stability and performance on a wider range of devices. This includes better handling of pipeline caches ([#176322](https://github.com/flutter/flutter/pull/176322)), fence waiters ([#173085](https://github.com/flutter/flutter/pull/173085)), and image layout transitions ([#173884](https://github.com/flutter/flutter/pull/173884)). + +### **Renderer unification** + +Work continues to unify the CanvasKit and Skwasm renderers. This release includes significant refactoring to share more code between the two, which will lead to a more consistent experience and faster development in the future ([#174588](https://github.com/flutter/flutter/pull/174588)). + +### Thread merging + +The ability to opt-out of thread merging has been removed from iOS and Android. For more information, check out [the great thread merge](https://www.youtube.com/watch?v=miW7vCmQwnw) video. + +## DevTools and IDEs + +### Experimental Widget Previews — Updates + +Flutter 3.35 introduced Widget Previews, an experimental feature ready for early feedback from the community. The Flutter 3.38 release brings significant improvements to Widget Previews, including: + +* **IDE integration**: Both our VSCode and Intellij / Android Studio plugins are updated with initial support for Widget Previews. You can now view your previews directly within your IDE for a more seamless development experience. + +<DashImage figure src="images/1NE0-VXAW-sVf3VESZEfZrw.webp" alt="*Widget Previews embedded in VSCode.*" caption="*Widget Previews embedded in VSCode.*" /> + + +When used within an IDE, the widget preview environment is configured by default to filter the displayed previews based on the currently selected source file: + +<DashImage figure src="images/1cvsiQjlzqc54_6D4HCsZCQ.gif" /> + + +* **Widget preview environment theming and control improvements**: The widget preview environment now supports light and dark modes, as well as custom IDE color schemes to match your development environment. Controls within the widget preview environment have also been adjusted to use less space, leaving more room available for rendering previews. + +<DashImage figure src="images/1eYq9jc4L08SjK4DYZx1qZQ.webp" alt="*Custom theming support for the Widget Previews environment.*" caption="*Custom theming support for the Widget Previews environment.*" /> + + +* **Preview extensibility**: The Preview annotation class is no longer marked as final and can now be extended to create custom Preview annotations, allowing for less boilerplate for common preview types. + +<DashImage figure src="images/1Hvcea8STYP87B_MK01-buA.webp" alt="*An example of a custom `BrightnessPreview` annotation.*" caption="*An example of a custom `BrightnessPreview` annotation.*" /> + + +* **MultiPreview support**: A new `MultiPreview` base class allows for creating multiple Preview variations from a single, custom annotation. + +<DashImage figure src="images/14Yk43MDOrzRUt3lfkVKY0g.webp" /> + + +* **Preview groups**: A new group parameter in the `Preview` class allows the grouping of related previews. + +<DashImage figure src="images/1SuEDY7F5DX6Ar2HQB523EQ.webp" alt="*An example of multiple “Brightness” previews in a preview group.*" caption="*An example of multiple “Brightness” previews in a preview group.*" /> + + +* **Reduced restrictions around @Preview annotation arguments**: Private constants are now supported as arguments to `Preview` annotations. Function arguments (such as wrapper and theme) are still required to have public, statically accessible names. + +Widget Previews is still an experimental feature, and your feedback is critical in shaping its future. The APIs and user experience are not yet stable and will change as we learn from you. + +Based on early feedback, more enhancements are planned to improve the widget preview experience, including: + +1. **Flutter DevTools Widget Inspector support**: The Widget Inspector is being updated to support inspecting previews within the widget preview environment. We plan to embed the inspector directly within the widget previewer, making it easily accessible regardless of your development environment. + +1. **Multi-project support in IDEs**: The widget previewer currently only supports displaying previews contained within a single project or Pub workspace. We’re actively investigating options to support IDE sessions with multiple Flutter projects ([issue #173550](https://github.com/flutter/flutter/issues/173550)). + +1. **Startup performance improvements**: Opportunities for performance improvements are being investigated to reduce initial startup times, including: + +* Launching a precompiled widget preview environment after the first run + +* Parallelizing preview detection logic to better handle large projects + +To get started, check out the documentation and let us know what you think! + +* **Read the docs**: [Getting Started with Flutter Widget Previews (Experimental)](https://docs.flutter.dev/tools/widget-previewer) + +* **Give feedback**: File issues and feature requests in the[ Flutter GitHub repository](https://github.com/flutter/flutter/issues/new/choose). + +* **Learn more**: For a technical deep-dive, see the[ Flutter Widget Previews Architecture document](https://flutter.dev/go/widget-previews-architecture). + +***Important Note**: There’s a known issue where the Widget Previewer can crash or stop updating after a `flutter pub get`. If you encounter this issue, run* `flutter pub get` *in your project and restart your IDE. See [#178317](https://github.com/flutter/flutter/issues/178317) for details.* + +### DevTools updates + +Flutter 3.38 contains fixes for some of the top pain points users called out in the 2025 DevTools user survey, including: + +**Network Panel Improvements** + +* Made it easier to understand when the panel is recording network traffic. ([#9495](https://github.com/flutter/devtools/pull/9495)) + +* Fixed issues around copy-pasting network requests. ([#9472](https://github.com/flutter/devtools/pull/9472), [#9482](https://github.com/flutter/devtools/pull/9482), [#9485](https://github.com/flutter/devtools/pull/9485), [#8588](https://github.com/flutter/flutter-intellij/pull/8588)) + +**Flutter Inspector fixes** + +* Fixed a bug where selecting a widget sometimes opened the underlying framework source code instead of the user’s source code. ([#176530](https://github.com/flutter/flutter/pull/176530)) + +* Fixed a bug occasionally preventing interaction with the the top buttons in the Inspector panel. ([#9327](https://github.com/flutter/devtools/pull/9327)) + +## Deprecations and breaking changes + +This release includes several important deprecations and breaking changes as part of the ongoing effort to modernize and improve the Flutter framework. + +Key build and tooling changes have been made that might affect custom build scripts. The `version` file at the root of the Flutter SDK has been removed in favor of a new `flutter.version.json` file located in `bin/cache` ([#172793](https://github.com/flutter/flutter/pull/172793)). Additionally, the `AssetManifest.json` file is no longer generated by default ([#172594](https://github.com/flutter/flutter/pull/172594)). + +Other notable changes include: + +* For more predictable behavior, a SnackBar that includes an action will no longer auto-dismiss ([#173084](https://github.com/flutter/flutter/pull/173084)). + +* The `OverlayPortal.targetsRootOverlay` constructor is deprecated in favor of the more flexible `OverlayPortal`(`overlayLocation: OverlayChildLocation.rootOverlay`). + +* Several properties on `CupertinoDynamicColor`, such as `withAlpha` and `withOpacity`, are now deprecated in favor of standard `Color` methods ([#171160](https://github.com/flutter/flutter/pull/171160)). + +* Flutter 3.38 requires Java 17 as the minimum version for Android, matching the [Gradle 8.14](https://docs.gradle.org/current/userguide/compatibility.html) (July 2025 release) minimum requirement. + +For more details and migration guidance on these and other changes, check out the [breaking changes page](https://docs.flutter.dev/release/breaking-changes). + +## Outro + +Flutter 3.38 is focused on making your day-to-day development faster and more enjoyable. These enhancements aim to streamline the way you build. We are incredibly grateful for the hard work and feedback from every community member who contributed to this release. + +For a comprehensive list of all the changes, be sure to check out the detailed breaking changes and release notes. To get a free boost to your productivity, simply run `flutter upgrade`! \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-3-41/images/146fO6mit5AFOq9ZY7YtRmA.webp b/sites/www/content/blog/whats-new-in-flutter-3-41/images/146fO6mit5AFOq9ZY7YtRmA.webp new file mode 100644 index 0000000000..c709e28946 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-41/images/146fO6mit5AFOq9ZY7YtRmA.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-41/images/1BU1kKyxkWidSLXOA37nPLg.webp b/sites/www/content/blog/whats-new-in-flutter-3-41/images/1BU1kKyxkWidSLXOA37nPLg.webp new file mode 100644 index 0000000000..d86ad670bf Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-41/images/1BU1kKyxkWidSLXOA37nPLg.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-41/images/1FyWSKI69_GcAv7MoAbPbww.webp b/sites/www/content/blog/whats-new-in-flutter-3-41/images/1FyWSKI69_GcAv7MoAbPbww.webp new file mode 100644 index 0000000000..fb636b8986 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-41/images/1FyWSKI69_GcAv7MoAbPbww.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-41/images/1XxO20lj9p2xlQfuIiwa8mg.webp b/sites/www/content/blog/whats-new-in-flutter-3-41/images/1XxO20lj9p2xlQfuIiwa8mg.webp new file mode 100644 index 0000000000..41e86252e4 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-41/images/1XxO20lj9p2xlQfuIiwa8mg.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-41/images/1YQySx5fHQJ9zs7_EI_Y4_g.webp b/sites/www/content/blog/whats-new-in-flutter-3-41/images/1YQySx5fHQJ9zs7_EI_Y4_g.webp new file mode 100644 index 0000000000..4f33ff64f3 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-41/images/1YQySx5fHQJ9zs7_EI_Y4_g.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-41/images/1baxbpT88lI2jDoF7fQmErQ.webp b/sites/www/content/blog/whats-new-in-flutter-3-41/images/1baxbpT88lI2jDoF7fQmErQ.webp new file mode 100644 index 0000000000..456a1b5d18 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-41/images/1baxbpT88lI2jDoF7fQmErQ.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-41/images/1jmKuW7ItWRJAHNABfTEhHA.gif b/sites/www/content/blog/whats-new-in-flutter-3-41/images/1jmKuW7ItWRJAHNABfTEhHA.gif new file mode 100644 index 0000000000..7576bd4498 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-41/images/1jmKuW7ItWRJAHNABfTEhHA.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-41/images/1klfeLTPZihrgBRUmUzWaIA.webp b/sites/www/content/blog/whats-new-in-flutter-3-41/images/1klfeLTPZihrgBRUmUzWaIA.webp new file mode 100644 index 0000000000..4447d6b2a0 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-41/images/1klfeLTPZihrgBRUmUzWaIA.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-41/images/1ocj9sgfhhYAYFlVIqPxZmA.webp b/sites/www/content/blog/whats-new-in-flutter-3-41/images/1ocj9sgfhhYAYFlVIqPxZmA.webp new file mode 100644 index 0000000000..5a81b7d77e Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-41/images/1ocj9sgfhhYAYFlVIqPxZmA.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-41/images/1tM9ItzXJAm4nN_OcyxzLCw.webp b/sites/www/content/blog/whats-new-in-flutter-3-41/images/1tM9ItzXJAm4nN_OcyxzLCw.webp new file mode 100644 index 0000000000..062ab38d10 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-41/images/1tM9ItzXJAm4nN_OcyxzLCw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-41/index.md b/sites/www/content/blog/whats-new-in-flutter-3-41/index.md new file mode 100644 index 0000000000..3c1db56185 --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-3-41/index.md @@ -0,0 +1,253 @@ +--- +title: "What’s new in Flutter 3.41" +description: "Empowering the community" +publishDate: 2026-02-11 +author: itsjustkevin +image: images/1jmKuW7ItWRJAHNABfTEhHA.gif +category: releases +layout: blog +--- + +<DashImage figure src="images/1XxO20lj9p2xlQfuIiwa8mg.webp" /> + + +## What’s new in Flutter 3.41 + +### Empowering the community + +Today, we’re excited to announce the release of Flutter 3.41, marking a transformative milestone in our journey together! What an exciting time to be a developer; this release represents our commitment to empowering our community in shaping the direction of Flutter. With 868 commits from 145 unique contributors, Flutter 3.41 provides additional transparency and modularity to further improve the experience of contributing to Flutter. + +We are introducing public release windows to make it easier for you to know when changes you contribute would land in a stable release. We are also continuing to decouple our design libraries, which in the long term will help us evolve design systems faster, while also giving you control over which design changes you adopt in your apps. Whether you are pushing the limits of the GPU with new fragment shader improvements or embedding Flutter seamlessly into existing native apps with content-sized views, this release ensures you have the tools to build with confidence and speed. + +There is so much to explore, so let’s dig right in… + +<DashImage figure src="images/1jmKuW7ItWRJAHNABfTEhHA.gif" /> + + +## Structural transparency and modularity + +### Public Release Windows + +Predictability is key to landing complex features safely. We are introducing public release windows to provide the entire community with the visibility needed to plan ahead. By explicitly calling out branch cutoff dates and release targets publicly, we enable the entire community to have clarity on when their changes would land in future stable releases. + +So what is a branch cutoff date? This date is the deadline for pull requests to land in the default branches (`main` for Dart and `master` for Flutter) to guarantee inclusion in the next stable release. If your PR merges before the cutoff, it will ship in the next stable version. If it merges after, it waits for the following cycle. + +For 2026, we plan to release four stable releases (including this one), the dates are as follows: + +* Flutter 3.41 — Feb | Branched on 06 January + +* Flutter 3.44 — May | Branches on 07 April + +* Flutter 3.47 — August | Branches on 07 July + +* Flutter 3.50 — November | Branches on 06 October + +### Decoupling for a leaner core + +We’re continuing our project to migrate the Material and Cupertino libraries to separate packages. This modular approach has several benefits: + +* **Faster release cycles:** We no longer need to wait for a quarterly SDK release to ship design updates. We can release new Material or Cupertino features and bug fixes the moment they are ready. + +* **Independent upgrades:** If you’re using an older SDK version due to a project constraint, you can still upgrade your design packages to get the latest look and feel. + +* **Adaptive design:** Versioned packages allow us to react much faster when iOS or Android introduce drastic design shifts like “Liquid Glass” or “Material 3 Expressive”, ensuring your app never looks out of date. + +Keep an eye on the [GitHub issue](https://github.com/flutter/flutter/issues/101479) to follow the progress. + +## Embracing ecosystem standards + +Part of empowering the community is ensuring Flutter plays nicely with the tools we already rely on. + +### Swift Package Manager and UIScene + +The transition from CocoaPods to Swift Package Manager continues. We strongly encourage [Plugin authors](https://docs.flutter.dev/packages-and-plugins/swift-package-manager/for-plugin-authors) to adopt Swift Package Manager, as it is now the standard for the Apple ecosystem. Additionally, to ensure compatibility with future iOS releases, Flutter now fully supports the [UIScene lifecycle by default](https://github.com/flutter/flutter/pull/178328) . This update is critical in meeting Apple’s requirements for upcoming iOS versions. To streamline migration from the deprecated AppDelegate lifecycle logic, check out our [migration guide](https://docs.flutter.dev/release/breaking-changes/uiscenedelegate). + +### Android Grade Plugin (AGP) 9 and Kotlin DSL + +We continue to align with modern Android standards. With the release of Android Gradle Plugin (AGP) 9, we are working on supporting new strictness requirements and providing guidance on how to navigate them. + +**Warning:** **Do not** update your Flutter app for Android to AGP 9 as **migrating plugins to AGP 9 and Flutter apps on AGP 9 using plugins** is not yet supported ([#181383)](https://github.com/flutter/flutter/issues/181383). This support is paused while the Flutter team audits the migration for backwards compatibility with older versions of AGP. This [migration doc](https://docs.flutter.dev/release/breaking-changes/migrate-to-agp-9) will be updated as guidance changes. + +Thanks to contributions from community member [Byoungchan Lee (bc-lee@)](https://github.com/bc-lee), new plugin projects now default to using the Kotlin DSL for Gradle. + +## Platform-specific assets + +Optimizing is now easier thanks to the work of [Alex Frei (hm21@)](https://github.com/hm21). You can now specify which platforms an asset should be bundled for in your pubspec.yaml. This allows optimizations such as, excluding heavy desktop assets from your mobile builds, significantly reducing app size. + +```yaml +flutter: + assets: + - path: assets/logo.png + - path: assets/web_worker.js + platforms: [web] + - path: assets/desktop_icon.png + platforms: [windows, linux, macos] +``` + + +## Listening to the community + +The Flutter team at Google continues to prioritize what matters most to you. + +### The getting started experience + +<DashImage figure src="images/1baxbpT88lI2jDoF7fQmErQ.webp" /> + + +For years, the community has called out that while Flutter is delightful and productive, it can be difficult to know where to begin the learning journey. + +We recently launched a completely reimagined [Dart and Flutter Getting Started Experience](https://blog.flutter.dev/announcing-our-new-dart-and-flutter-getting-started-experience-b8c4b2be0984). + +It’s a new learning pathway designed to guide you through the fundamentals of building apps with Flutter and Dart. Let’s take a quick look at some of the changes: + +* We’ve introduced a new “quick install” guide that leverages hot-reload on the web, enabling learners to experience the full power of Flutter fast, without setting up a platform-specific environment first. A pathway consisting of written tutorials, low-stakes quizzes, and eight new videos featuring some familiar faces from the Flutter team at Google. + +* Learners would finish the learning path having built 4 apps completely from scratch. + +Give the new Getting Started Experience a try in the newly redesigned [Learn](https://docs.flutter.dev/learn/pathway) section of our website, powered by [Jaspr](https://jaspr.site/). + +### Fragment shader improvements + +In Q4 2025, we surveyed developers using the `FragmentShader` API and asked simply: *What is impeding your work?* You told us that you needed improved ergonomics when using the API, while also having more flexibility when defining your shaders. In response to that, we made the following changes: + +* In 3.41, we added Synchronous Image Decoding. Previously, creating textures for shaders could introduce a frame of lag. With `decodeImageFromPixelsSync`, you can now generate textures and use them as samplers in the same frame. + +* We’ve also added support for High Bitrate Textures (up to 128-bit float), unlocking the ability to use high-resolution lookup tables (LUTs) for GPU-accelerated photo filters and SDFs. + +```cpp +void attachTexture(ui.FragmentShader shader) { + ui.PictureRecorder recorder = ui.PictureRecorder(); + Canvas canvas = Canvas(recorder); + canvas.drawCircle(const Offset(64, 64), 64, Paint()..color = Colors.red); + ui.Picture picture = recorder.endRecording(); + ui.Image image = picture.toImageSync( + 128, + 128, + targetFormat: ui.TargetPixelFormat.rFloat32, + ); + shader.setImageSampler(0, image); +} +``` + + +### Widget Previews (Experimental) + +Based on your early feedback, we are iterating rapidly on Widget Previews. With this release, Widget Previews have improved as follows: + +* **Flutter Inspector support:** The Widget Preview environment now has access to an embedded Flutter Inspector instance, making it easy to inspect layouts and the state of previewed widgets. **Important note:** it might be necessary to configure additional package directories to see widgets from your project by default. To do this, open the Flutter Inspector settings by clicking the gear icon and add a new package directory pointing to your project. + +<DashImage figure src="images/1FyWSKI69_GcAv7MoAbPbww.webp" /> + + +* **Support for applications with `dart:ffi` dependencies:** Previously, previews that included widgets with transitive dependencies on libraries importing `dart:ffi` would cause compilation errors and prevent the preview environment from updating. This occurred because `dart:ffi` isn’t supported on web platforms ([flutter/flutter#166431](https://github.com/flutter/flutter/issues/166431)). The widget previewer can now handle previews that have dependencies on platform-specific libraries, including `dart:ffi` and `dart:io`. **Important note**: invoking APIs from these libraries isn’t supported in the widget previewer and will result in an error being displayed for previews calling these platform-specific APIs. See the [Dart documentation on conditional imports](https://dart.dev/tools/pub/create-packages#conditionally-importing-and-exporting-library-files) for examples of how to write code that’s compatible with both native and web platforms. + +## Framework fidelity and polish + +We continue to refine the present experience with a focus on platform fidelity and developer efficiency. + +### iOS polish + +In Flutter 3.41 we’ve made visual improvements with the new “bounded blur” style. Previously, translucent widgets using the `BackdropFilter` could suffer from color bleeding at the edges. Thanks to improvements in the Impeller rendering engine, we have eliminated this artifact. + +<DashImage figure src="images/1YQySx5fHQJ9zs7_EI_Y4_g.webp" /> + + +<DashImage figure src="images/1tM9ItzXJAm4nN_OcyxzLCw.webp" /> + + +We’ve also added support for native styled drag handling for `CupertinoSheet` through the `showDragHandle` property. + +<DashImage figure src="images/1ocj9sgfhhYAYFlVIqPxZmA.webp" /> + + +### Add-to-App + +Adding Flutter views to existing Android and iOS apps is now easier! Flutter views embedded in existing native apps can now automatically resize themselves based on their content. Previously, a Flutter view needed a fixed size provided by its native parent. This made some scenarios difficult, like adding Flutter views to a native scrollable view. + +To use this, your root widget must support unbounded constraints. Avoid using widgets that require a pre-defined size (like `ListView` or `LayoutBuilder`) at the top of your tree, as they will conflict with the dynamic sizing logic. + +To enable this behavior on iOS, [set`FlutterViewController.isAutoResizable` to true](https://docs.flutter.dev/add-to-app/ios/add-flutter-screen#content-sized-views). For Android, enable content sizing in your Android Manifest and [set the width or height of your FlutterView to content_wrap.](https://docs.flutter.dev/add-to-app/android/add-flutter-view#content-sized-views) + +<DashImage figure src="images/1BU1kKyxkWidSLXOA37nPLg.webp" /> + + +<DashImage figure src="images/146fO6mit5AFOq9ZY7YtRmA.webp" /> + + +### Navigation and scrolling + +We’ve worked hard to smooth out the rough edges in core interaction models through the following changes: + +* We’ve introduced `Navigator.popUntilWithResult`, allowing you to pop multiple screens and pass a value back to the destination route in a single call, drastically simplifying state management in multi-step flows. + +* We reimplemented `StretchingOverscrollIndicator` using a simulation-based approach ported from Android 12. This ensures a more natural and fluid overscroll effect that responds correctly to high-velocity flings. + +<DashImage figure src="images/1klfeLTPZihrgBRUmUzWaIA.webp" /> + + +* We fixed an issue with pinned headers in `NestedScrollView` and `SliverMainAxisGroup`, ensuring headers correctly overlap subsequent slivers. + +## Accessibility + +Helping you build accessible experiences that reach users on any screen is at the heart of our mission. In this update, we’ve added the following: + +* Native accessibility support for `CircularProgressIndicator` and `LinearProgressIndicator`, allowing assistive technologies to announce progress updates. + +* Flutter now respects web users’ text spacing overrides to improve the reading experience. + +* We’ve introduced new matchers like `isSemantics` and `accessibilityAnnouncement` in flutter_test to make validating accessibility easier. + +## Material and animation + +We have introduced new primitives and properties to expand control over animations and layout. Thanks to the work of community member [Bernardo Ferrari (bernaferrari@)](https://github.com/bernaferrari), `RepeatingAnimationBuilder` introduces a declarative way to create continuous animations like a loading indicator, a pulsing button, or a shimmering placeholder effect. + +For example, here’s how you can make a square slide back-and-forth: + +```dart +RepeatingAnimationBuilder<Offset>( + animatable: Tween<Offset>( + begin: const Offset(-1.0, 0.0), + end: const Offset(1.0, 0.0), + ), + duration: const Duration(seconds: 1), + repeatMode: RepeatMode.reverse, + curve: Curves.easeInOut, + builder: (BuildContext context, Offset offset, Widget child) { + return FractionalTranslation( + translation: offset, + child: child, + ); + }, + child: const ColoredBox( + color: Colors.green, + child: SizedBox.square(dimension: 100), + ), +), +``` + + +We also updated `CarouselView` with a `.builder` constructor, making it easier to create carousels with dynamic content. `DropdownMenuFormField` now supports a custom `errorBuilder`, and `RawAutoComplete` now includes an `OptionsViewOpenDirection.mostSpace` option to intelligently position options based on available screen space. + +## Community-led evolution + +One of the strongest examples of Flutter open source partnerships is our longstanding relationship with Canonical. Their team continues to drive the Flutter Desktop roadmap forward, delivering critical features that benefit the entire ecosystem. + +Thanks to Canonical’s engineering leadership, we are closing the gap on complex desktop UI requirements. This release introduces experimental APIs for creating popup windows and tooltip windows, along with cross-platform support for dialog windows on Linux, macOS, and Windows. Finally, new APIs were added to let you test multi-window apps. If you’d like a sneak peek of these upcoming windowing APIs, check out Flutter’s [multiple_windows example app](https://github.com/flutter/flutter/tree/master/examples/multiple_windows)! + +Flutter Linux now also enables merged threads by default, simplifying the threading model and improving performance while also working to improve Windows stability. We intend to remove the merged threads opt-out in a future release. If you have any issues with merged threads, please [let us know](https://github.com/flutter/flutter/issues/150525). + +## DevTools + +Devtools has seen improvements in performance and stability: + +* Flutter’s DevTools are compiled using dart2wasm, resulting in performance improvements. You can still opt out and revert to the dart2js-compiled DevTools by using the settings dialog. + +* Dropped connections to the Dart Tooling Daemon (DTD) are now automatically retried to improve the experience when your machine resumes after sleeping. + +These are just a couple highlights from this release. To learn about all the updates included in Flutter 3.41, check out the release notes for DevTools [2.52.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.52.0), [2.53.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.53.0), and [2.54.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.54.0). + +## Next Stop: `flutter upgrade` + +Flutter 3.41 is a step toward improving our open source contributing experience, while also improving Flutter’s foundational feature to enhance your development experience whether you are migrating a plugin to Swift Package Manager, optimizing assets for specific platforms, or pushing pixels with new Shader APIs. We are grateful for the amazing community we’ve built together. + +For a comprehensive list of all the changes, be sure to check out the detailed [breaking changes](https://docs.flutter.dev/release/breaking-changes) and [release notes](https://docs.flutter.dev/release/release-notes/release-notes-3.41.0). To try out the new features in Flutter 3.41, all it takes is a `flutter upgrade`! \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/13z_r0es7chMz1dSDxT8zsg.webp b/sites/www/content/blog/whats-new-in-flutter-3-44/images/13z_r0es7chMz1dSDxT8zsg.webp new file mode 100644 index 0000000000..2538fed3be Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/13z_r0es7chMz1dSDxT8zsg.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/16n-KEHq1VjiglHNYTPV-yQ.gif b/sites/www/content/blog/whats-new-in-flutter-3-44/images/16n-KEHq1VjiglHNYTPV-yQ.gif new file mode 100644 index 0000000000..9251ffb2eb Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/16n-KEHq1VjiglHNYTPV-yQ.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/17uz0MAnNLfnKOmRGweNcgg.webp b/sites/www/content/blog/whats-new-in-flutter-3-44/images/17uz0MAnNLfnKOmRGweNcgg.webp new file mode 100644 index 0000000000..62994bd595 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/17uz0MAnNLfnKOmRGweNcgg.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1AWOBIU0sytlgElyB3n-x9w.webp b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1AWOBIU0sytlgElyB3n-x9w.webp new file mode 100644 index 0000000000..f65c3593cf Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1AWOBIU0sytlgElyB3n-x9w.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1BbFM0AsTzm5tN8ZSbC-6Aw.webp b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1BbFM0AsTzm5tN8ZSbC-6Aw.webp new file mode 100644 index 0000000000..9337684f3d Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1BbFM0AsTzm5tN8ZSbC-6Aw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1HeoTr2vo8Ua7b2kL-Qn1-Q.webp b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1HeoTr2vo8Ua7b2kL-Qn1-Q.webp new file mode 100644 index 0000000000..1b08f2132a Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1HeoTr2vo8Ua7b2kL-Qn1-Q.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1Je2nftTI2SbCoW0AybhYiQ.webp b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1Je2nftTI2SbCoW0AybhYiQ.webp new file mode 100644 index 0000000000..cc45c59d0c Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1Je2nftTI2SbCoW0AybhYiQ.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1Pdl2nE7Nidvg-oVf8wwKwA.webp b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1Pdl2nE7Nidvg-oVf8wwKwA.webp new file mode 100644 index 0000000000..e452a3b483 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1Pdl2nE7Nidvg-oVf8wwKwA.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1TAkSiptrLpWXi-x8V6perA.webp b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1TAkSiptrLpWXi-x8V6perA.webp new file mode 100644 index 0000000000..cba71e47ad Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1TAkSiptrLpWXi-x8V6perA.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1VdQhRElD22jiVdNmjLllhw.webp b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1VdQhRElD22jiVdNmjLllhw.webp new file mode 100644 index 0000000000..ec95324af8 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1VdQhRElD22jiVdNmjLllhw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1WEch_2CJNkOt1NChpXZlZw.webp b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1WEch_2CJNkOt1NChpXZlZw.webp new file mode 100644 index 0000000000..b365d0a148 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1WEch_2CJNkOt1NChpXZlZw.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1ZEXlGLmZ6hccimLUin4Jow.gif b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1ZEXlGLmZ6hccimLUin4Jow.gif new file mode 100644 index 0000000000..ace216615a Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1ZEXlGLmZ6hccimLUin4Jow.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1grXWhNW3A7_zzko3y-oGEA.jpeg b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1grXWhNW3A7_zzko3y-oGEA.jpeg new file mode 100644 index 0000000000..8cb4c9464a Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1grXWhNW3A7_zzko3y-oGEA.jpeg differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1gz1il93fimwoqoeeWXnROA.webp b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1gz1il93fimwoqoeeWXnROA.webp new file mode 100644 index 0000000000..5f17091a8b Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1gz1il93fimwoqoeeWXnROA.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1ihBR-fBmc1L-kQdlnAKPNA.webp b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1ihBR-fBmc1L-kQdlnAKPNA.webp new file mode 100644 index 0000000000..ed20401634 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1ihBR-fBmc1L-kQdlnAKPNA.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1jge3MA91e1nbNjncqu9dtA.webp b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1jge3MA91e1nbNjncqu9dtA.webp new file mode 100644 index 0000000000..30515df602 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1jge3MA91e1nbNjncqu9dtA.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1lQx3GCLqTFbDzK4meOjo1Q.webp b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1lQx3GCLqTFbDzK4meOjo1Q.webp new file mode 100644 index 0000000000..2a1f1dbc46 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1lQx3GCLqTFbDzK4meOjo1Q.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1lx61h88hYLIWaeSYuIQ14g.webp b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1lx61h88hYLIWaeSYuIQ14g.webp new file mode 100644 index 0000000000..e215518fc0 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1lx61h88hYLIWaeSYuIQ14g.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1qw6H8YVAmVFUWuI0b38bZg.webp b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1qw6H8YVAmVFUWuI0b38bZg.webp new file mode 100644 index 0000000000..37c826606f Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1qw6H8YVAmVFUWuI0b38bZg.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1uq-70ckWhRQm15bI26Evfg.webp b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1uq-70ckWhRQm15bI26Evfg.webp new file mode 100644 index 0000000000..33eed20520 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1uq-70ckWhRQm15bI26Evfg.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1vZ-dmpSHSo3nF0ChZDb02A.gif b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1vZ-dmpSHSo3nF0ChZDb02A.gif new file mode 100644 index 0000000000..d73b8a0b07 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1vZ-dmpSHSo3nF0ChZDb02A.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/images/1xNy4ENkOUVx7fotyFcG8_A.webp b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1xNy4ENkOUVx7fotyFcG8_A.webp new file mode 100644 index 0000000000..4cfa29bf05 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-44/images/1xNy4ENkOUVx7fotyFcG8_A.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-44/index.md b/sites/www/content/blog/whats-new-in-flutter-3-44/index.md new file mode 100644 index 0000000000..7341d3a581 --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-3-44/index.md @@ -0,0 +1,564 @@ +--- +title: "What’s new in Flutter 3.44" +description: "Empowering developers at Google I/O 2026" +publishDate: 2026-05-20 +author: khanhnwin +image: images/1vZ-dmpSHSo3nF0ChZDb02A.gif +category: releases +layout: blog +--- + +<DashImage figure src="images/1vZ-dmpSHSo3nF0ChZDb02A.gif" alt="Flutter 3.44: Scaling to more users on more devices!" caption="Flutter 3.44: Scaling to more users on more devices!" /> + + +## What’s new in Flutter 3.44 + +Empowering developers at Google I/O 2026 + +Flutter 3.44 is here, and it’s a big one! This release celebrates a milestone in the Flutter timeline: Hybrid Composition++ for Android, Swift Package Manager as the new iOS/macOS default, and improved Vulkan support for Impeller. We’re previewing multi-window desktop support with Canonical as our new lead maintainer, and embarking on a major architectural evolution by decoupling Material and Cupertino from the core framework. We’re redefining UX for agentic user experiences with GenUl and reimagining developer experience with Agentic Hot Reload and Dart & Flutter Agent Skills. Flutter is empowering the next generation of apps-everywhere from the 2026 Toyota RAV4 multimedia system to the upcoming LG webOS SDK. We’re so excited to share all of the news and updates with you; welcome to Flutter 3.44! + +<DashImage figure src="images/1gz1il93fimwoqoeeWXnROA.webp" alt="Flutter is everywhere, everyday, built by everyone, for everyone." caption="Flutter is everywhere, everyday, built by everyone, for everyone." /> + + +This year’s Flutter theme at Google I/O is: Flutter is everywhere, everyday, built by everyone, for everyone. + +“Everywhere, everyday” stems from a Tuesday realization I had using an app on my phone: Flutter apps were part of my life everywhere, from tracking my meals in the Bay Area to shopping while I was in Japan. The numbers back this up: The pub.dev ecosystem is more popular than ever, with over **1.3 billion package downloads**, in the last 30 days alone. Flutter is now the **second most popular development SDK for mobile** on both major app stores with over **1.5 million monthly developers**, a 50% increase in just one year. + +“Built by everyone, for everyone” reflects a chat with my Google I/O co-host, Kate Lovett, about the joy of our global community. Our **1,700+ committed and passionate contributors** are the engine behind this progress, landing **5,800 changes in the core repository** this past year. In this release cycle alone, we’ve landed **972 commits** from **178 unique contributors**, including **61 new contributors** landing their first commits. Our community remains the lifeblood of Flutter, ensuring it truly is built by everyone, for everyone. THANK YOU! + +There are lots of changes to tell you about. You might also want to check out the [changes in the Dart 3.12 release](https://dart.dev/blog/announcing-dart-3-12). + +## Developer experience + +We want to make the experience of building apps with Flutter as productive as possible, whether you’re writing code by hand or iterating with your favorite coding agent. In this release, we are improving upon our existing suite of developer tools and introducing new ones to augment your development workflows. + +### DevTools performance improvements + +We’re introducing fine-grained analysis to boost efficiency and we’ve made performance improvements for analysis of projects with many files or directories. + +We’ve also added more stability and performance improvements to Flutter DevTools, including a change to use WASM by default, which makes DevTools snappier and more responsive. + +**Learn more**: Release notes for DevTools [2.55.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.55.0) and [2.57.0](https://docs.flutter.dev/tools/devtools/release-notes/release-notes-2.57.0). + +### Widget Previews (experimental) + +This release brings further performance improvements and features to the Widget Preview environment: + +* **Preview detection rewrite**: The detection logic now utilizes the Dart Analysis Server, significantly reducing memory usage by the flutter tool. For IDE users, overall memory usage should be reduced by up to 50%. + +* **Preview filtering**: It’s now possible to filter previews by group, name, and script and package URIs, making it easier to work in projects with many previews. Special thanks to community member[ NamanGoyalK](https://github.com/NamanGoyalK) for this[ contribution](https://github.com/flutter/flutter/pull/184023)! + +<DashImage figure src="images/1AWOBIU0sytlgElyB3n-x9w.webp" alt="Widget Previews enables you to preview standalone widgets in isolation, separate from your full app" caption="Widget Previews enables you to preview standalone widgets in isolation, separate from your full app" /> + + +**Learn more**: [Flutter Widget Previewer](https://docs.flutter.dev/tools/widget-previewer) + +### Rosetta-free native Apple Silicon support + +Mac developers running Apple Silicon chips no longer need to install the Rosetta translation layer to run Flutter. All macOS command-line tools, including our iOS device communication binaries, have been updated to run natively on ARM. This update arrives ahead of Apple’s planned deprecation of Rosetta translation support, ensuring your development environment is future-proofed on Apple hardware. Looking ahead, a future release of Flutter will end support for Intel x86 Macs entirely. If your team still relies on Intel-based Mac hosts for development, you should begin planning your hardware migration. + +**Learn more**: [Using Intel-based apps on a Mac with Apple silicon](https://support.apple.com/en-us/102527) (support.apple.com) + +### Reimagining developer experience in an AI-driven world + +Over the last year, the Dart and Flutter ecosystem has witnessed an explosion of agent-based tools like Antigravity, Gemini CLI, Claude Code, and Cursor which are fundamentally evolving the developer’s role into one of architecting and coordinating with agents. To support this transition, we’ve focused on enhancing our developer experience foundation and introducing new tools to augment your development workflows. + +**Seamless coding agent integration with Agentic Hot Reload** + +In a big leap forward for AI-assisted development, we are launching Agentic Hot Reload: The MCP server and your favorite coding agent will now automatically find and connect to running Dart and Flutter applications. This means coding agents like Antigravity can now hot-reload your running app out of the box! When you prompt your AI assistant to edit a UI, the agent writes the code and triggers a hot reload automatically to show you the result instantly, no manual setup needed. Go ahead and try it! We’ve also… + +* **Hardened dependency search**: Agents can now read and search files inside package dependencies safely, without requiring full access to your local pub cache. + +* **Consolidated tools**: We consolidated our MCP tool definitions, significantly reducing token costs for your agent workflows. + +Check out Agentic Hot Reload in action: + +<DashImage figure src="images/16n-KEHq1VjiglHNYTPV-yQ.gif" alt="Agentic Hot Reload: You can prompt your agent to make a change, and it now automatically connects to and hot reloads your running app" caption="Agentic Hot Reload: You can prompt your agent to make a change, and it now automatically connects to and hot reloads your running app" /> + + +We also recently launched **Agent Skills for Dart and Flutter**, equipping your coding agents with task-oriented, production-grade domain expertise. These skills level up your coding agent and help you save tokens when completing tasks such as adding integration tests or setting up localization, while adhering to recommended best practices. + +**Learn more**: [Introducing skills for Dart and Flutter](https://blog.flutter.dev/introducing-skills-for-dart-and-flutter-23837c6ec0ae), [Dart and Flutter MCP server](https://docs.flutter.dev/ai/mcp-server) + +<DashImage figure src="images/1qw6H8YVAmVFUWuI0b38bZg.webp" alt="Dart & Flutter Agent Skills give your agent step-by-step instructions on how to complete a variety of tasks, like writing integration tests" caption="Dart & Flutter Agent Skills give your agent step-by-step instructions on how to complete a variety of tasks, like writing integration tests" /> + + +### AI on every screen with Flutter: Building the next generation of AI-native apps + +As AI-driven features evolve from simple content summaries to fully agentic assistants, we are focused on expanding the Dart and Flutter ecosystem to provide the infrastructure needed for these experiences on every platform. + +**Firebase AI Logic** + +Firebase AI Logic enables you to call the Gemini API client-side from your Flutter apps. + +[MacroFactor](https://macrofactor.com/) is a Flutter app that uses [Firebase AI Logic](https://pub.dev/packages/firebase_ai) to connect directly to a Gemini model and leverage its multimodal capabilities. I’ve been using it to track my meals, simply by taking a picture. It’s a great example of an app that’s used AI to turn a tedious chore into a delightful, seemingly magical user experience. + +Firebase AI Logic now features [Server Prompt Templates](https://firebase.google.com/docs/ai-logic/server-prompt-templates/get-started?api=dev#high-level-use-template-in-code), removing the need to embed prompts directly into your application code. + +[Firebase Agent Skills for Flutter](https://firebase.google.com/docs/ai-assistance/agent-skills) are now available, providing step-by-step guidance to help you build full-stack Flutter and Firebase applications more effectively. + +<DashImage figure src="images/1uq-70ckWhRQm15bI26Evfg.webp" alt="MacroFactor is a Flutter app that uses Firebase AI Logic to make API calls directly to Gemini models, leveraging its multimodal vision capabilities to simplify the user journey for logging meals." caption="MacroFactor is a Flutter app that uses Firebase AI Logic to make API calls directly to Gemini models, leveraging its multimodal vision capabilities to simplify the user journey for logging meals." /> + + +**Learn more:** [MacroFactor revolutionizes nutrition with AI for 400k+ users with Firebase, Flutter, and Gemini](https://cloud.google.com/customers/macrofactor) + +**Genkit Dart in preview** + +We’re also excited to share the preview launch of Genkit Dart, an open-source framework for building full-stack, AI-powered and agentic apps. It has a model-agnostic API that supports providers like Google, Anthropic, and OpenAI. It comes with everything you need to go from prototype to production, including type-safe structured output, tool calling, multi-turn conversations, and built-in observability. + +You can run Genkit Dart server-side or client-side in Flutter apps, too! + +```cpp +import 'package:genkit/genkit.dart'; +import 'package:genkit_google_genai/genkit_google_genai.dart'; + +void main() async { + final ai = Genkit(plugins: [googleAI()]); + + final response = await ai.generate( + model: googleAI.gemini('gemini-flash-latest'), + prompt: 'Why is Dart a great language for AI + applications?', + ); + + print(response.text); +} +``` + + +**Learn more**: [Genkit Dart: Build full-stack AI apps with Dart and Flutter](https://dart.dev/blog/announcing-genkit-dart-build-full-stack-ai-apps-with-dart-and-flutter) + +### Gemma 3n Impact Challenge + +We’re incredibly proud to see Flutter developers pushing the boundaries of what’s possible with AI. A huge congratulations to Tommaso Giovannini, creator of Gemma Vision, and Guido Marangoni, creator of Vite Vere, for their first and second place wins in the [Gemma 3n Impact Challenge](https://www.kaggle.com/competitions/google-gemma-3n-hackathon) last year. Both chose Flutter to build life-changing tools: + +* Gemma Vision helps those with visual impairments perceive the world + +* Vite Vere assists those with cognitive disabilities in completing tasks in their daily lives. + +<DashImage figure src="images/1ihBR-fBmc1L-kQdlnAKPNA.webp" alt="Congratulations to Tomasso, creator of Gemma Vision, and Guido, creator of Vite Vere. They won first and second place, respectively, in the Gemma 3n Impact Challenge!" caption="Congratulations to Tomasso, creator of Gemma Vision, and Guido, creator of Vite Vere. They won first and second place, respectively, in the Gemma 3n Impact Challenge!" /> + + +**Learn more**: [Gemma 3n Impact Challenge winners](https://www.kaggle.com/competitions/google-gemma-3n-hackathon/hackathon-winners) + +### Gemma 4 + +Gemma 4 recently launched, it’s a lightweight, on-device model purpose-built for advanced reasoning and agentic workflows, cost, on-device data constraints, or network constraints. Its multimodal capabilities are fantastic, and I’ve been especially impressed by its ability to do multi-step planning and chaining tool calls. + +Historically, managing these on-device models across diverse hardware was complex. This is why I’m so excited about LiteRT-LM. + +**Learn more**: [Gemma 4](https://deepmind.google/models/gemma/gemma-4/) + +### LiteRT-LM for Flutter + +<DashImage figure src="images/1HeoTr2vo8Ua7b2kL-Qn1-Q.webp" /> + + +As I was digging into the code for both Gemma Vision and [Vite Vere](https://ai.google.dev/competition/projects/vite-vere), it was inspiring to see both leveraging the `flutter_gemma`, a plugin available from pub.dev, to integrate with Gemma. + +It’s only going to get better: we’re excited to share that full LiteRT-LM support for Flutter is coming soon to the [`flutter_gemma`](https://pub.dev/packages/flutter_gemma) package. + +LiteRT-LM is Google’s production-ready, high-performance, open-source inference framework. This will abstract hardware differences away and enable you to run powerful on-device AI models like Gemma 4 across devices, all while ensuring peak performance with GPU and NPU acceleration across all 6 of Flutter’s stable platforms: Android, iOS, Web, Windows, Linux, and macOS. + +**Learn more**: [`flutter_gemma`](https://pub.dev/packages/flutter_gemma) package and [LiteRT-LM](https://ai.google.dev/edge/litert-lm/overview). + +### Flutter + A2UI = GenUI + +When it comes to AI-driven user experiences, we can all agree that we’re exhausted by walls of markdown — or worse, plaintext. + +Generative UI, or GenUI, is a UX paradigm where AI constructs and responds with real-time UI, rather than just text, as seen here in the Hatcha Demo app. + +<DashImage figure src="images/1ZEXlGLmZ6hccimLUin4Jow.gif" alt="Hatcha is a social event planning app powered by GenUI for Flutter. Hosts plan through a conversational interview while GenUI generates themed invites, tailored components, and planning modules, all shaped by your event and its audience." caption="Hatcha is a social event planning app powered by GenUI for Flutter. Hosts plan through a conversational interview while GenUI generates themed invites, tailored components, and planning modules, all shaped by your event and its audience." /> + + +Over the last year, our GenUI team has been pushing this forward as a project partner defining the emerging [A2UI protocol](https://a2ui.org/). A2UI is an open-source protocol from Google that defines how an agent and a client collaborate on the composition and state of a user interface. + +Since launching the Flutter GenUI SDK late last year, we’ve seen incredible momentum, with package downloads up 500% since the beginning of the year. + +A standout example is Catagay Ulusoy’s **Finnish it** ([Google Play store](https://play.google.com/store/apps/details?id=com.sopuacademy.finnishit&hl=en_US), [Apple store](https://apps.apple.com/us/app/finnish-it-yki-test-practice/id6742380858)). This app doesn’t just create custom lesson plans to help users learn Finnish; it dynamically composes the perfect UI on the fly for each lesson. If you caught the [Cloud Next Developer Keynote](https://www.youtube.com/watch?v=A01DQ8_xy7Q) last month, you might’ve seen Flutter DevRel lead Emma Twersky give the app and Catagay a well-deserved shoutout! + +<DashImage figure src="images/1grXWhNW3A7_zzko3y-oGEA.jpeg" alt="Flutter DevRel lead Emma Twersky giving the “Finnish It!” app a shoutout in the Google Cloud Next Developer Keynote!" caption="Flutter DevRel lead Emma Twersky giving the “Finnish It!” app a shoutout in the Google Cloud Next Developer Keynote!" /> + + +**Learn more**: [`genui`](https://pub.dev/packages/genui) package + +**The Visual Layout experiment** + +Li-Te Cheng and his team from Google DeepMind were pioneers in the GenUI space. Remember [this demo](https://youtu.be/v5tRc_5-8G4?si=WZd3l0ZwLUKt1elU&t=97) that made its rounds within Flutter circles way back in 2023 because of the red debug banner in the demo? Yep, that was Li-Te’s team! + +He joined us for the What’s New in Flutter talk to share his experience building the Gemini App’s “Visual Layout” experiment with Flutter. Here’s the web version: + +<DashImage figure src="images/1WEch_2CJNkOt1NChpXZlZw.webp" alt="Starting from an initial user prompt, the Visual Layout Experiment encouraged users to select, click, and explore information customized and updated on-the-fly through Generative UI." caption="Starting from an initial user prompt, the Visual Layout Experiment encouraged users to select, click, and explore information customized and updated on-the-fly through Generative UI." /> + + +He touched on why his team tends to reach for Flutter as their UI toolkit of choice… Hint: It’s the same reason that we all love Flutter: + +* Beautiful UI + +* Productive developer experience + +* Multiplatform support + +* An architecture that is perfectly suited for GenUI (Li-Te’s words, not mine! 😉) + +Here were his 3 key takeaways that you can use for your own GenUI projects: + +1. Lean on opinionated frameworks for AI consistency + +1. Use an “AI critic” loop to ensure reliable outputs + +1. Balance speed and control with templates. + +In closing, Li-Te challenged us to go beyond walls of text and chatbots, and instead, build rich, interactive, delightful experiences. + +**Learn more**: [GenUI SDK for Flutter](https://pub.dev/packages/genui) and, if you want a guided tutorial, [try out the Codelab](https://codelabs.developers.google.com/codelabs/genui-intro?hl=en#0). + +## Android support + +### Googlebook and peripheral support + +Flutter is already equipped to handle the new Googlebook laptop, powered by Gemini. Because Flutter targets Android’s large-screen guidelines, apps handle external hardware inputs naturally. Trackpad scrolling, mouse hover states, right-click menus, and keyboard shortcuts work by default. Because Flutter has mature desktop support across macOS, Windows, and Linux, Flutter apps will feel native and responsive on a Googlebook rather than looking like stretched mobile ports. Existing mobile apps will feel at home on a Googlebook without requiring an extensive rewrite. + +**Learn more**: [Introducing Googlebook, designed for Gemini intelligence](https://blog.google/products-and-platforms/platforms/android/meet-googlebook/) + +### Android 17 + +Android 17 is on the horizon, and the team is actively testing Flutter against the latest Android 17 betas to ensure your apps continue to work as expected. We’re also proactively integrating the newest security and usability features like Local Network Protections and safe Dynamic Code Loading. + +You can monitor our ongoing progress on GitHub. We encourage you to [download the Android 17 beta](https://developer.android.com/about/versions/17/get) and start testing your apps today. If you run into bugs or notice any missing features, please [file an issue!](https://github.com/flutter/flutter/issues/new/choose) + +**Learn more**: [Android 17 GitHub project](https://github.com/orgs/flutter/projects/248/) + +### Hybrid Composition++ for Android + +Embedding native Android components like web views or maps into a Flutter app has historically forced developers to choose between frame rate and fidelity. Older rendering strategies struggled with screen tearing during scrolls, broken text input, or high CPU overhead. + +Flutter 3.44 introduces Hybrid Composition++ (HCPP) as an opt-in feature to solve these problems. Instead of relying on offscreen buffers or forcing the Flutter engine to process native views, HCPP delegates layer compositing directly to the Android OS. The process takes advantage of the Vulkan graphics library’s low level access using hardware buffer swapchains and `SurfaceControl` transactions to synchronize the Flutter UI with the native Android views. + +The result is high-performance scrolling and accurate touch input. It also brings reliable support for `SurfaceView` components, which presented challenges for older modes. + +<DashImage figure src="images/1xNy4ENkOUVx7fotyFcG8_A.webp" alt="HC on the left, HCPP on the right" caption="HC on the left, HCPP on the right" /> + + +HCPP has [Android API and hardware requirements](https://docs.flutter.dev/platform-integration/android/platform-views#hcpp), so not all devices can use HCPP even when opted in. There are no new APIs to adopt, and you simply need to enable the flag to upgrade your existing platform views. You can test the new mode before it becomes the default rendering mode in the future by passing the `--enable-hcpp` flag to your `run` command, or by adding the configuration flag to your `AndroidManifest.xml` file: + +```kotlin +<meta-data + android:name="io.flutter.embedding.android.EnableHcpp" + android:value="true" /> +``` + + +**Learn more**: [Hosting native Android views in your Flutter app with Platform Views](https://docs.flutter.dev/platform-integration/android/platform-views) + +### Android display corner radii support + +To help you build pixel-perfect layouts on modern mobile devices, Flutter now integrates directly with Android hardware to support display corner radii ([#179219](https://github.com/flutter/flutter/pull/179219)). Flutter can now query the physical and logical corner radii of the device’s display and expose this information through `MediaQuery`. This allows your UI to accurately respect the hardware’s geometry, ensuring content is never clipped on aggressively rounded screens. + +**Learn more**: [`MediaQueryData.displayCornerRadii`](https://main-api.flutter.dev/flutter/widgets/MediaQueryData/displayCornerRadii.html) + +### Android Gradle Plugin 9.0 and built-in Kotlin + +Prior to Android Gradle plugin (AGP) 9, Android app and plugin developers had to manually add the Kotlin Gradle plugin (KGP) to their build files so the system could understand and compile Kotlin code. As of AGP 9.0, the Android build system handles Kotlin natively. Because the build system already knows how to process Kotlin, manually adding the separate KGP now creates a conflict and causes a build failure. This breaking change affects apps and Flutter plugins that apply KGP. + +The Flutter team added temporary backwards compatibility to keep existing projects building safely. Support for manually applying KGP will be removed in a future release. + +**Instructions for app developers** + +If you develop Flutter apps, you need to update your Android build files to remove the separate Kotlin Gradle plugin (KGP). + +Note: If your migrated app uses a Flutter plugin that still applies KGP, your build will fail. Since only the plugin author can fix this, please [report the issue to the plugin author](https://docs.flutter.dev/release/breaking-changes/migrate-to-built-in-kotlin/for-app-developers#report-incompatible-kotlin-gradle-plugin-usage-to-plugin-authors). + +**Learn more**: For full step-by-step instructions, see the [app developer migration guide](https://docs.flutter.dev/release/breaking-changes/migrate-to-built-in-kotlin/for-app-developers). + +**Instructions for plugin authors** + +The migration process for plugins requires similar Gradle file changes, plus an important version constraint update. To ensure compatibility, you must update your `pubspec.yaml` file to *set a minimum Flutter version constraint of 3.44*. + +**Learn more**: For the full checklist, refer to the [plugin author migration guide](https://docs.flutter.dev/release/breaking-changes/migrate-to-built-in-kotlin/for-plugin-authors). + +**ABI filtering changes** + +ABIs determine which device hardware architectures (like ARM or x86) your compiled app supports. Flutter used to apply ABI filters programmatically to every specific build type, but now configures them once in the base `defaultConfig` block. Because the AGP 9 combines the default configuration with specific build types and flavors instead of overriding it, using custom ABI settings requires an extra step. + +If your app uses custom `abiFilters` inside specific build types or product flavors, you now need to pass the `-Pdisable-abi-filtering=true` flag when building or running your app. + +**Learn more**: For more details, see the [flavors guide](https://docs.flutter.dev/deployment/flavors#add-unique-build-settings). + +## iOS support + +<DashImage figure src="images/1TAkSiptrLpWXi-x8V6perA.webp" alt="Swift Package Manager is now the default" caption="Swift Package Manager is now the default" /> + + +### Swift Package Manager is now the default for iOS and macOS + +Starting with Flutter 3.44, Swift Package Manager (SwiftPM) replaces CocoaPods as the default dependency manager for iOS and macOS apps. The Flutter CLI handles this migration automatically. When you build or run your app, the CLI updates your Xcode project to use SwiftPM, removing the need to manage Ruby or CocoaPods installations! + +**Learn more**: [Say goodbye to CocoaPods: Swift Package Manager is soon the default in Flutter!](https://blog.flutter.dev/saying-goodbye-to-cocoapods-swift-package-manager-is-soon-the-default-in-flutter-645a92714a57) + +Add-to-App integration is also more flexible. If you embed Flutter into an existing iOS application, the new `flutter build swift-package` command packages your Flutter app or add-to-app module as a Swift Package for easy consumption in your native project. + +**Learn more**: View the [updated documentation](https://docs.flutter.dev/add-to-app/ios/project-setup) to learn how to integrate with SwiftPM. + +If your app relies on plugins that still require CocoaPods, the Flutter CLI will print a warning and temporarily fall back to CocoaPods for those dependencies. We recommend asking those package maintainers to update, as CocoaPods support will eventually be removed entirely. To encourage ecosystem adoption, packages with SwiftPM support are now awarded additional points in pub.dev scoring. + +If you maintain an iOS or macOS plugin, you need to add SwiftPM support to your package. If you migrated during the 2024 pilot, make sure you also add `FlutterFramework` as a dependency in your `Package.swift` file. + +If SwiftPM causes a breaking issue for your project, you can temporarily disable it by setting `--enable-swift-package-manager: false` in your `pubspec.yaml`. If you use this opt-out, please [file a bug report on GitHub](https://github.com/flutter/flutter/issues/new/choose) with your Xcode project files so we can investigate. Please note that this opt-out feature will eventually be removed. + +**Learn more**: [Swift Package Manager for plugin authors](https://docs.flutter.dev/packages-and-plugins/swift-package-manager/for-plugin-authors) + +### Flutter supports UIScene + +Starting with iOS 13, Apple introduced the “Scene”-based lifecycle to support multi-window experiences. During WWDC 2025, Apple announced that apps built with the latest SDK will soon be required to use the `UIScene` lifecycle to launch. This update is critical in meeting Apple’s requirements for upcoming iOS versions. + +There are no new changes in Flutter 3.44, but we would like to remind you to migrate before Apple starts enforcing this new API by default. The Flutter CLI automatically migrates your app if your `AppDelegate` hasn’t been customized. However, if your code tinkers with UI lifecycle events, you should follow the full migration guide. + +**Learn more**: [UISceneDelegate migration guide](https://docs.flutter.dev/release/breaking-changes/uiscenedelegate) + +### iOS predictive text (experimental) + +We are introducing experimental support for native iOS inline predictive text for text input fields ([#183650](https://github.com/flutter/flutter/pull/183650)). This feature is off by default, but you can opt-in and test it by enabling `TextField.enableInlinePrediction`. When enabled, users typing in your app can accept iOS-predicted text (for example, typing “My n” and accepting “ame”) by pressing the **Space** key. Please note that the visual styling for this predictive text is still under active polish, and we appreciate your feedback as we finalize this feature. + +**Learn more**: [`TextField.enableInlinePrediction`](https://main-api.flutter.dev/flutter/material/TextField/enableInlinePrediction.html) + +## Web + +### Accessibility + +Accessibility and user preference parity have also been greatly enhanced, featuring out-of-the-box support for the browser’s `prefers-reduced-motion` setting to automatically disable animations, alongside immediate screen reader feedback for form validation errors using `aria-description`. + +**Learn more**: [prefers-reduced-motion](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/prefers-reduced-motion) CSS media feature (mozilla.org) + +### Platform and tooling + +Development workflows and browser integration have never been smoother. The engine now handles `autofill` in iOS 26 Safari by reusing DOM forms across focus shifts, while refining web scrolling and keyboard event synthesis for robust reliability. Furthermore, the CLI streamlines web app orchestration by bringing `--base-href` support directly to `flutter run`, matching production build configurations. + +**Learn more**: [PR #182024](https://github.com/flutter/flutter/pull/182024), [PR #179703](https://github.com/flutter/flutter/pull/179703), [PR #180692](https://github.com/flutter/flutter/pull/180692) + +## Desktop support + +<DashImage figure src="images/13z_r0es7chMz1dSDxT8zsg.webp" alt="Canonical will lead the Flutter Desktop roadmap and oversee the maintenance of our Linux, Windows, and macOS embedders." caption="Canonical will lead the Flutter Desktop roadmap and oversee the maintenance of our Linux, Windows, and macOS embedders." /> + + +**We are excited to announce an expanded partnership with Canonical, who will now serve as the lead maintainer and Strategic Steward for Flutter Desktop.** With their deep technical expertise, Canonical will lead the Flutter Desktop roadmap and oversee the maintenance of our Linux, Windows, and macOS embedders. + +This collaboration represents just the first step in a broader ecosystem expansion. Moving forward, we are actively expanding our governance with more partners to bring Flutter’s high-performance, multi-platform experience to even more environments and industries. + +Stay tuned to hear more about this partnership! + +To inquire about partnering with the Flutter team, contact [partners@flutter.dev](mailto:partners@flutter.dev). + +### Windowing APIs (experimental) + +*⚠️ **Note:** Windowing features are currently only available on the [main channel](https://docs.flutter.dev/install/upgrade#switching-flutter-channels). They are not yet intended for production use.* + +<DashImage figure src="images/1jge3MA91e1nbNjncqu9dtA.webp" alt="Flutter now supports Tooltips and separate Dialog windows across platforms" caption="Flutter now supports Tooltips and separate Dialog windows across platforms" /> + + +Canonical continues to make excellent progress on desktop’s experimental windowing APIs! New features include: + +* **Tooltips**: Flutter now supports tooltip windows across Linux, macOS, and Windows ([#182348](https://github.com/flutter/flutter/pull/182348), [#180895](https://github.com/flutter/flutter/pull/180895), [#179147](https://github.com/flutter/flutter/pull/179147)). + +* **Popups**: Flutter now supports popup windows on macOS ([#182371](https://github.com/flutter/flutter/pull/182371)), with support for Linux and Windows expected in a future release. + +* **Dialogs**: Material’s `showDialog` function now creates a separate child dialog window on platforms that support windowing ([#181861](https://github.com/flutter/flutter/pull/181861)). + +Finally, content-sized views are now supported on Linux ([#182924](https://github.com/flutter/flutter/pull/182924)). This lets you size a window dynamically based on its content, which is useful for popup or tooltip windows. + +**Learn more**: For an early sneak peek of desktop’s experimental windowing APIs, check out the [`multiple_windows` example](https://github.com/flutter/flutter/tree/master/examples/multiple_windows). + +### Windows stylus support + +Windows apps built with Flutter got a major upgrade for digital artists and note-takers! Thanks to a brilliant contribution from community member [CodeDoctorDE](https://github.com/CodeDoctorDE), Flutter Windows now supports stylus input, including precise tracking for both stylus rotation and pressure sensitivity. + +**Learn more**: [PR 165323: Allow stylus support on Windows](https://github.com/flutter/flutter/pull/165323) + +## Embedded + +### Toyota + +**The [Toyota RAV4 was the world’s best selling car in 2025](https://www.kbb.com/car-news/report-toyota-rav4-takes-worlds-best-selling-car-title/). Now, the 2026 RAV4 is using Flutter to power its multimedia system.** + +Last month, I experienced one of the highlights of my career: The opportunity to travel to Plano, TX and visit the Toyota Motor North America & Toyota Connected offices to talk to the engineering team about how Flutter has changed the game for them when it comes to designing, building, and delivering multimedia systems: from the test units in their offices to cars in driveways. As both a Flutter engineer and a car nerd who grew up in a family that only purchased Toyotas, I’m ecstatic to see Flutter running on the 2026 RAV4. I’ve seen them so many times as I’m out and about. (ahem-everywhere??) + +Thank you to the Toyota Motor North America and Toyota Connected teams for hosting us! + +Check out the showcase video: + +<YoutubeEmbed id="X4376aLFHFA" title="How Toyota is revolutionized their infotainment system with Flutter" fullwidth="true"/> + + +<DashImage figure src="images/1lQx3GCLqTFbDzK4meOjo1Q.webp" alt="Flutter Outbound product manager, Abdallah, and I getting our photos taken on the Toyota Test Track!" caption="Flutter Outbound product manager, Abdallah, and I getting our photos taken on the Toyota Test Track!" /> + + +**Learn more**: Toyota’s press release, [The Latest Evolution of Toyota’s Multimedia Coming to a Screen Near You](https://pressroom.toyota.com/the-latest-evolution-of-toyotas-multimedia-coming-to-a-screen-near-you/) + +### LG + +<DashImage figure src="images/1Pdl2nE7Nidvg-oVf8wwKwA.webp" alt="The LG webOS SDK will enable developers build Flutter apps that target WebOS devices" caption="The LG webOS SDK will enable developers build Flutter apps that target WebOS devices" /> + + +LG is on the verge of launching the webOS SDK to help developers easily build Flutter apps that target WebOS devices, empowering Flutter for the big screen and beyond. + +The webOS SDK will include plugin support for Firebase, video players, gamepads, and more. It’ll even come with support for all of the Flutter features that you know and love, like stateful hot reload and state management with Riverpod. + +Keep an eye out for this exciting launch in the coming weeks! + +## Graphics and engine enhancements + +This release brings targeted rendering and performance enhancements to the Impeller backend. + +### Impeller improvements + +**Vulkan** + +This release includes several Vulkan improvements including better memory management of caches and more efficient GPU/CPU synchronization in cases where frames are being dropped. + +**Cleaner circles with SDFs** + +The math for rendering circles has been updated to support cleaner circles using signed-distance functions. There were cases where they would previously be aliased, but that has been resolved. ([#183536](https://github.com/flutter/flutter/pull/183536), [#183184](https://github.com/flutter/flutter/pull/183184)) + +<DashImage figure src="images/1Je2nftTI2SbCoW0AybhYiQ.webp" alt="Enhanced visual fidelity, utilizing signed-distance functions (SDFs), to ensure high-quality, anti-aliased rendering for complex shapes." caption="Enhanced visual fidelity, utilizing signed-distance functions (SDFs), to ensure high-quality, anti-aliased rendering for complex shapes." /> + + +**Shadow and perspective fixes** + +Improvement on how Impeller handles perspective matrices, correcting rendering behaviors for shadows and perspective projection transforms. ([#181434](https://github.com/flutter/flutter/pull/181434), [#183187](https://github.com/flutter/flutter/pull/183187)) + +### FragmentShader improvements + +Writing fragment shaders is now more intuitive and less error-prone, thanks to the following enhancements. + +**Get Uniform by Name API** + +You can now bind uniform variables in your shaders by name instead of manual offsets, dramatically simplifying shader code setup: + +```csharp + void setUp(ui.FragmentShader shader) { + shader.getUniformFloat('foobar').set(1.234); + } +``` + + +Learn more: [Writing and using FragmentShaders](https://docs.flutter.dev/ui/design/graphics/fragment-shaders), [`FragmentShader.getUniformFloat`](https://main-api.flutter.dev/flutter/dart-ui/FragmentShader/getUniformFloat.html) + +**Clearer shader compiler diagnostics** + +The shader compiler now generates warnings when compiling shaders that aren’t compatible with Skia, helping you identify cross-platform rendering issues before deploying ([#182786](https://github.com/flutter/flutter/pull/182786), [#183146](https://github.com/flutter/flutter/pull/183146)). + +## Framework + +This release balances significant architectural shifts with a rigorous focus on quality and community-driven refinements. As we begin the strategic decoupling of the Material and Cupertino libraries to standalone packages, the core framework continues to mature with major updates to web rendering, foundational stability improvements, and enhanced platform integration. + +### Material and Cupertino updates + +This release marks a massive milestone for the Material and Cupertino libraries. These libraries have been frozen as of this release, representing the final set of updates to these libraries within the core framework before they transition to standalone packages: `material_ui` and `cupertino_ui`. By the next stable release, the versions of these libraries currently in the framework will be deprecated, and you will be able to migrate to the new, independently-versioned packages. + +**Learn more**: For more information on this transition, read the [blog post about the freeze](https://blog.flutter.dev/flutters-material-and-cupertino-code-freeze-d32d94c59c38) and follow the [main tracking issue for decoupling](https://github.com/flutter/flutter/issues/172932) these libraries from the core framework. + +Despite the freeze, this release is packed with refinements. A major highlight is the modernization of menus in the Cupertino library. The new `CupertinoMenuAnchor` widget, built on the flexible `RawMenuAnchor` foundation, provides a more robust and native-feeling menu experience for iOS applications ([#182036](https://github.com/flutter/flutter/pull/182036)). This work was made possible by the extensive contributions of community member [davidhicks980](https://github.com/davidhicks980), who also created the RawMenuAnchor widget. + +<DashImage figure src="images/17uz0MAnNLfnKOmRGweNcgg.webp" alt="Example of CupertinoMenuAnchor in action." caption="Example of CupertinoMenuAnchor in action." /> + + +**Learn more**: [`CupertinoMenuAnchor`](https://main-api.flutter.dev/flutter/cupertino/CupertinoMenuAnchor-class.html) + +On the Material side, menus also receive a polish with the addition of Material 3 animations to the `MenuAnchor` class. These animations provide a smoother, more responsive feel, and a new `hoverOpenDelay` parameter on `SubmenuButton` gives you finer control over submenu interactions. The animation is disabled by default, and is enabled by setting animated to true. ([#176494](https://github.com/flutter/flutter/pull/176494)). + +<DashImage figure src="images/1VdQhRElD22jiVdNmjLllhw.webp" alt="Thee addition of Material 3 animations to the MenuAnchor class." caption="Thee addition of Material 3 animations to the MenuAnchor class." /> + + +**Learn more**: [`MenuAnchor`](https://main-api.flutter.dev/flutter/material/MenuAnchor-class.html), [`SubmenuButton.hoverOpenDelay`](https://main-api.flutter.dev/flutter/material/SubmenuButton/hoverOpenDelay.html) + +This release also enables scrollable content within a `CupertinoSheetRoute` to work seamlessly with the dragging animation, allowing for a more fluid transition between scrolling and dismissing the sheet ([#177337](https://github.com/flutter/flutter/pull/177337)). For developers needing custom drag regions, the new `scrollableBuilder` allows you to pass the managed `ScrollController` to the body’s scrollable areas to coordinate sheet dragging for you. + +<DashImage figure src="images/1BbFM0AsTzm5tN8ZSbC-6Aw.webp" alt="Scrollable content within a `CupertinoSheetRoute` working seamlessly with the dragging animation" caption="Scrollable content within a `CupertinoSheetRoute` working seamlessly with the dragging animation" /> + + +**Learn more**: [`CupertinoSheetRoute`](https://api.flutter.dev/flutter/cupertino/CupertinoSheetRoute-class.html), [`CupertinoSheetRoute.scrollableBuilder`](https://main-api.flutter.dev/flutter/cupertino/CupertinoSheetRoute/scrollableBuilder.html) + +The `CarouselView` component sees major functional improvements in this release. It now supports infinite scrolling ([#175710](https://github.com/flutter/flutter/pull/175710)), allowing you to create seamlessly looping carousels. It also features a new `onIndexChanged` callback and a `leadingItem` property on its controller, providing better visibility into the carousel’s state as the user interacts with it ([#180667](https://github.com/flutter/flutter/pull/180667)). + +<DashImage figure src="images/1lx61h88hYLIWaeSYuIQ14g.webp" alt="CarouselView now supports infinite scrolling!" caption="CarouselView now supports infinite scrolling!" /> + + +**Learn more**: [`CarouselView`](https://main-api.flutter.dev/flutter/material/CarouselView-class.html) + +New design primitives make it easier to achieve sophisticated UI effects, such as the new `ShapedInputBorder`. This allows Material widgets to create an input border using a shape by specifying any `ShapeBorder`. This can be useful, for example, for making the Material input border appear in an iOS style using `RoundedSuperellipseBorder`. ([#177220](https://github.com/flutter/flutter/pull/177220)). Similarly, `CupertinoFocusHalo` now supports the superellipse shape, ensuring consistent focus indicators across different widget geometries ([#180724](https://github.com/flutter/flutter/pull/180724)). + +**Learn more**: [`ShapedInputBorder`](https://main-api.flutter.dev/flutter/material/ShapedInputBorder-class.html) + +Several existing widgets have also been refined. The `Expansible` widget, which powers Material’s `ExpansionTile` under the hood, is now more capable. A new toggle method is now available on both `ExpansibleController` and `ExpansionTileController`, accompanied by improved documentation and examples ([#181320](https://github.com/flutter/flutter/pull/181320), [#180273](https://github.com/flutter/flutter/pull/180273)). Additionally, Material’s list tiles, `RadioListTile`, `CheckboxListTile`, and `SwitchListTile`, now correctly accept a `WidgetStatesController`, allowing for more programmatic control over their visual states ([#180367](https://github.com/flutter/flutter/pull/180367)). + +### Accessibility: a more inclusive experience for all users + +Making applications accessible to everyone remains a core priority of the Flutter framework. This release introduces deeper integration with platform-specific accessibility settings, improves the precision of semantic announcements, and refines the accessibility of common UI components. + +For iOS developers, this release adds support for several new accessibility motion features ([#178102](https://github.com/flutter/flutter/pull/178102)). Your applications can now respond to user preferences for: + +* **Auto-play animated images**: Detects when a user prefers to pause automatically playing GIFs or other animated content. + +* **Auto-play video previews**: Informs the app if the user has disabled the automatic playback of video previews. + +* **Prefer non-blinking cursor**: Allows the app to provide a stable, non-blinking text indicator for users who find blinking cursors distracting or difficult to track. + +* These settings are exposed with the `AccessibilityFeatures` object, enabling you to build more responsive and respectful UIs on iOS. + +Progress indicators also receive a quality-of-life improvement. You can now use percentage strings (such as “50%”) as a `SemanticsValue` for `ProgressIndicator` ([#183670](https://github.com/flutter/flutter/pull/183670)). This allows screen readers to announce progress in a more natural and human-readable format, rather than just raw decimal values. + +This release also polishes the semantics of core widgets. The `Slider` widget’s semantics node has been refactored to more accurately reflect its size and position, improving the experience for users who navigate by touch exploration or assistive devices ([#184168](https://github.com/flutter/flutter/pull/184168)). Additionally, a fix for scroll views ensures that invisible accessibility elements are no longer incorrectly presented before the scrollable content, leading to a cleaner and more predictable navigation flow ([#184155](https://github.com/flutter/flutter/pull/184155)). + +Together, these changes ensure that Flutter applications continue to provide a high-quality, inclusive experience across all platforms. + +### Resiliency for zero-width/height widgets + +A major effort in this release has been dedicated to improving the framework’s stability when widgets are rendered in “0x0 environments” — scenarios where widgets are given zero width or height, which could previously trigger layout errors or unexpected crashes. Thanks to the gradual, steady contributions of community member [ahmedsameha1](https://github.com/ahmedsameha1), we have added zero-size coverage across many core widgets, including `Hero` ([#180954](https://github.com/flutter/flutter/pull/180954)), `Icon` ([#181021](https://github.com/flutter/flutter/pull/181021)), `AnimatedPadding` ([#181235](https://github.com/flutter/flutter/pull/181235)), and `GridPaper` ([#180906](https://github.com/flutter/flutter/pull/180906)). These updates ensure that your applications remain resilient during complex layout transitions or inside highly constrained viewports. + +### SelectableRegion improvements + +We have resolved two key issues inside `SelectableRegion` to improve layout fidelity and text selection behavior across native and web platforms: + +**Web layout constraint preservation** + +Previously, `SelectableRegion` could cause its children to shrink unexpectedly when rendering on the web. It now correctly passes all layout constraints unmodified to its children, ensuring consistent sizing behavior ([#184083](https://github.com/flutter/flutter/pull/184083)). + +**Multiline copy precision** + +Text selection within `SelectableRegion` is now more precise — when users select and copy text that spans multiple lines, line breaks are now properly preserved in the copied output instead of being lost ([#184421](https://github.com/flutter/flutter/pull/184421)). + +## Breaking changes and deprecations + +This release includes several important deprecations and breaking changes as part of the ongoing effort to modernize and improve the Flutter framework. + +### RawMenuAnchor callback adjustments + +The calling order of some callbacks of `RawMenuAnchor` has been adjusted to allow more flexible and predictable customization. + +**Learn more**: [Changing RawMenuAnchor close order](https://docs.flutter.dev/release/breaking-changes/raw-menu-anchor-close-order) + +Key deprecations in this release include: + +* **`CupertinoSheetRoute`**: The `builder` and `pageBuilder` parameters in `showCupertinoSheet` and `CupertinoSheetRoute` are now deprecated in favor of `scrollableBuilder` ([#177337](https://github.com/flutter/flutter/pull/177337)). This change allows for better integration of scrollable content with the sheet’s dragging animations. + +* **`ReorderableListView`**: The `onReorder` callback is deprecated in favor of the more precise `onReorderItem` ([#178242](https://github.com/flutter/flutter/pull/178242)). The new callback provides a more predictable `newIndex` that accounts for the item being removed before being re-inserted. + +* **Tooling**: The`--web-hot-reload` flag in the Flutter tool is now deprecated, as hot reload for web is now handled through more modern mechanisms ([#181884](https://github.com/flutter/flutter/pull/181884)). Additionally, the `plugin_ffi` template is deprecated in favor of the more robust plugin template with FFI support ([#181588](https://github.com/flutter/flutter/pull/181588)). + +**Learn more**: For more details and migration guidance on these and other changes, see the [breaking changes page](https://docs.flutter.dev/release/breaking-changes) on flutter.dev. + +## Flutter is everywhere, everyday. + +Flutter’s reach extends across mobile, desktop, web, and embedded systems, and while individual features are impressive, they collectively deliver a robust platform for developers — empowering over 1.5 million developers to build incredible, user experiences that are used everywhere, everyday. You can find Flutter in everything from business tools and daily apps like [NotebookLM](https://www.youtube.com/watch?v=pMoUg3dkDJk&list=PLjxrf2q8roU1yXu4k7ivSLAa0cizD4feH&index=2&pp=iAQB), [Talabat](https://www.youtube.com/watch?v=A3ApoV_QRio&list=PLjxrf2q8roU1yXu4k7ivSLAa0cizD4feH&index=1&t=1s&pp=iAQB), [Zoho](https://www.youtube.com/watch?v=YWAuwAR28KY), and [Karaca](https://www.youtube.com/watch?v=N8I-fdOYWnc&pp=0gcJCQQLAYcqIYzv), to high-profile embedded implementations like the infotainment system in the 2026 Toyota RAV4 and LG’s webOS devices. + +## Flutter is built by everyone, for everyone. + +Flutter’s success is built on your feedback! We’re committed to keeping that conversation going — whether through comments, issues, or our upcoming developer survey. Your input is what drives the features you love, so please keep sharing it with us. + +This ecosystem is depended upon by and made up of industry leaders like LG, Toyota, and Canonical — and most importantly, by the over 1.5 million developers building with Flutter everyday. We are so excited to continue building and growing this wonderful Dart & Flutter ecosystem together. + +To try out all the new features, optimizations, and graphics enhancements, all it takes is a simple: + +```typescript +flutter upgrade +``` diff --git a/sites/www/content/blog/whats-new-in-flutter-3-7/images/07rMA1GAEoQxfkwYC.webp b/sites/www/content/blog/whats-new-in-flutter-3-7/images/07rMA1GAEoQxfkwYC.webp new file mode 100644 index 0000000000..2308216e5c Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-7/images/07rMA1GAEoQxfkwYC.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-7/images/08T3nUAK-8AUveoD2.webp b/sites/www/content/blog/whats-new-in-flutter-3-7/images/08T3nUAK-8AUveoD2.webp new file mode 100644 index 0000000000..e03ad4b5d9 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-7/images/08T3nUAK-8AUveoD2.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-7/images/0FIlFtTa6DAlXUwqE.webp b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0FIlFtTa6DAlXUwqE.webp new file mode 100644 index 0000000000..d13b0d5e10 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0FIlFtTa6DAlXUwqE.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-7/images/0LmVDzMTW34id5CNV.webp b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0LmVDzMTW34id5CNV.webp new file mode 100644 index 0000000000..2aee16b620 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0LmVDzMTW34id5CNV.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-7/images/0OQtroLJAJcetxyeV.webp b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0OQtroLJAJcetxyeV.webp new file mode 100644 index 0000000000..01ed8a8009 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0OQtroLJAJcetxyeV.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-7/images/0P_s-0gE5JGUerDkA.webp b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0P_s-0gE5JGUerDkA.webp new file mode 100644 index 0000000000..1ff4864230 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0P_s-0gE5JGUerDkA.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-7/images/0SY_JFBrqhg_Gs64Z.gif b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0SY_JFBrqhg_Gs64Z.gif new file mode 100644 index 0000000000..12f490015d Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0SY_JFBrqhg_Gs64Z.gif differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-7/images/0VSZRJbLyqlfekARB.webp b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0VSZRJbLyqlfekARB.webp new file mode 100644 index 0000000000..9011d38da4 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0VSZRJbLyqlfekARB.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-7/images/0dYBqWaVEAscMJ2Yz.webp b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0dYBqWaVEAscMJ2Yz.webp new file mode 100644 index 0000000000..e4ce045a26 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0dYBqWaVEAscMJ2Yz.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-7/images/0i5K5F5bxN_5X0EAi.webp b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0i5K5F5bxN_5X0EAi.webp new file mode 100644 index 0000000000..c702719865 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0i5K5F5bxN_5X0EAi.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-7/images/0kdYw1nYkT8aKHjZP.webp b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0kdYw1nYkT8aKHjZP.webp new file mode 100644 index 0000000000..e664949d7f Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0kdYw1nYkT8aKHjZP.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-7/images/0nMxlWBkYUzlFdnI2.webp b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0nMxlWBkYUzlFdnI2.webp new file mode 100644 index 0000000000..9e7f0e43a2 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0nMxlWBkYUzlFdnI2.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-7/images/0oLSR3-_9jQzCozYV.webp b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0oLSR3-_9jQzCozYV.webp new file mode 100644 index 0000000000..2dbba887ee Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0oLSR3-_9jQzCozYV.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-7/images/0op4bHcx7HvorMKSE.webp b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0op4bHcx7HvorMKSE.webp new file mode 100644 index 0000000000..04dddf3905 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0op4bHcx7HvorMKSE.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-7/images/0uq8A_Zq5BM2DGDRT.webp b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0uq8A_Zq5BM2DGDRT.webp new file mode 100644 index 0000000000..337e9d3dd6 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0uq8A_Zq5BM2DGDRT.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-7/images/0vuIpoOEp5lqEZYeW.webp b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0vuIpoOEp5lqEZYeW.webp new file mode 100644 index 0000000000..558f2183b1 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-7/images/0vuIpoOEp5lqEZYeW.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-7/images/1AsyYVtFMXY0iS6gLz_O4BA.png b/sites/www/content/blog/whats-new-in-flutter-3-7/images/1AsyYVtFMXY0iS6gLz_O4BA.png new file mode 100644 index 0000000000..02e30aa8b2 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3-7/images/1AsyYVtFMXY0iS6gLz_O4BA.png differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3-7/index.md b/sites/www/content/blog/whats-new-in-flutter-3-7/index.md new file mode 100644 index 0000000000..dae24d93fa --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-3-7/index.md @@ -0,0 +1,316 @@ +--- +title: "What’s new in Flutter 3.7" +description: "Material 3 updates, iOS improvements, and much more!" +publishDate: 2023-01-24 +author: itsjustkevin +image: images/1AsyYVtFMXY0iS6gLz_O4BA.png +category: releases +layout: blog +--- + +We are excited to start 2023 with the release of Flutter 3.7! In Flutter 3.7, we as a community, continue to improve the framework with the addition of some great new features such as: the ability to create custom menu bars, cascading menus, tools to better support internationalization, new debugging tools, and much more. + +We also continue to refine features such as global selection, faster rendering with Impeller, DevTools, and as always, performance! + +Let’s go on a quick journey together to explore the new features in Flutter 3.7! + +## Enhanced Material 3 support + +Material 3 support has been greatly enhanced in 3.7 with the migration of the following widgets: + +* [`Badge`](https://api.flutter.dev/flutter/material/Badge-class.html) + +* [`BottomAppBar`](https://api.flutter.dev/flutter/material/BottomAppBar-class.html) + +* [`Filled`](https://api.flutter.dev/flutter/material/FilledButton-class.html) and [`Filled Tonal`](https://api.flutter.dev/flutter/material/FilledButton/FilledButton.tonal.html) buttons + +* [`SegmentedButton`](https://api.flutter.dev/flutter/material/SegmentedButton-class.html) + +* [`Checkbox`](https://api.flutter.dev/flutter/material/Checkbox-class.html) + +* [`Divider`](https://api.flutter.dev/flutter/material/Divider-class.html) + +* [`Menus`](https://api.flutter.dev/flutter/material/MenuBar-class.html) + +* [`DropdownMenu`](https://api.flutter.dev/flutter/material/DropdownMenu-class.html) + +* [`Drawer`](https://api.flutter.dev/flutter/material/Drawer-class.html) and [`NavigationDrawer`](https://api.flutter.dev/flutter/material/NavigationDrawer-class.html) + +* [`ProgressIndicator`](https://api.flutter.dev/flutter/material/ProgressIndicator-class.html) + +* [`Radio`](https://api.flutter.dev/flutter/material/Radio-class.html) buttons + +* [`Slider`](https://api.flutter.dev/flutter/material/Slider-class.html) + +* [`SnackBar`](https://api.flutter.dev/flutter/material/SnackBar-class.html) + +* [`TabBar`](https://api.flutter.dev/flutter/material/TabBar-class.html) + +* [`TextFields`](https://api.flutter.dev/flutter/material/TextField-class.html) and [`InputDecorator`](https://api.flutter.dev/flutter/material/InputDecorator-class.html) + +* [`Banner`](https://api.flutter.dev/flutter/widgets/Banner-class.html) + +To use these new features just turn on the [`useMaterial3`](https://api.flutter.dev/flutter/material/ThemeData/useMaterial3.html) flag in your application’s `[ThemeData](https://api.flutter.dev/flutter/material/ThemeData-class.html) widget.` To take full advantage of M3 support you will want a complete M3 color scheme. You can provide your own, use the new [theme builder](https://m3.material.io/theme-builder#/custom) tool, or Flutter can generate one for you from a single seed color using the `colorSchemeSeed` parameter of the [`ThemeData`](https://api.flutter.dev/flutter/material/ThemeData-class.html) constructor: + +```less +MaterialApp( + theme: ThemeData( + useMaterial3: true, + colorSchemeSeed: Colors.green, + ), + // … +); +``` + + +For the latest details of Flutter’s Material 3 support, see the [umbrella issue](https://github.com/flutter/flutter/issues/91605) on GitHub. + +To play with these components yourself, check out the [interactive demo](https://flutter.github.io/samples/web/material_3_demo/) showing off all the new M3 features: + +<DashImage figure src="images/1AsyYVtFMXY0iS6gLz_O4BA.png" /> + + +## Menu bars and cascading menus + +Flutter can now create menu bars and cascading context menus. + +For macOS, create a menu bar using the `PlatformMenuBar` widget, which defines platform native menu bars rendered by macOS instead of Flutter. + +And, for all platforms, you can define a [Material Design menu](https://m3.material.io/components/menus/overview) that provides cascading menu bars ([`MenuBar`](https://api.flutter.dev/flutter/material/MenuBar-class.html)), or standalone cascading menus triggered by another user interface element ([`MenuAnchor`](https://api.flutter.dev/flutter/material/MenuAnchor-class.html)). These menus are fully customizable, and the menu items can be custom widgets, or you can use the new menu item widgets ([`MenuItemButton`](https://api.flutter.dev/flutter/material/MenuItemButton-class.html), [`SubmenuButton`](https://api.flutter.dev/flutter/material/SubmenuButton-class.html)). + +<DashImage figure src="images/08T3nUAK-8AUveoD2.webp" /> + + +## Impeller preview + +The team is pleased to announce that the new [Impeller rendering engine](https://github.com/flutter/engine/tree/main/impeller) is [ready for preview](https://github.com/flutter/engine/tree/main/impeller#try-impeller-in-flutter) on iOS on the stable channel. We believe the performance of Impeller will meet or exceed the Skia renderer for most apps, and as for fidelity, Impeller implements all but a small number of rarely used corner cases. We expect to make Impeller the default renderer on iOS in a forthcoming stable release, so please continue to file [Impeller feedback](https://github.com/flutter/flutter/issues) on GitHub. + +While we are increasingly confident that Impeller on iOS will meet the rendering needs of nearly all existing Flutter apps, there are still a few gaps in API coverage. The small number of remaining gaps are listed [on the Flutter wiki](https://github.com/flutter/flutter/wiki/Impeller#status). Users might also notice minor visual differences in rendering between Skia and Impeller. These minor differences might be bugs, so please don’t hesitate to [file issues](https://github.com/flutter/flutter/issues). + +Our progress on Impeller was greatly accelerated by contributions from the community. In particular, GitHub users [ColdPaleLight](https://github.com/ColdPaleLight), [guoguo338](https://github.com/guoguo338), [JsouLiang](https://github.com/JsouLiang), and [magicianA](https://github.com/magicianA) contributed 37 out of 291 (>12%) Impeller-related patches to this release. Thank you! + +We continue to make progress on a Vulkan backend for Impeller (with fallback to OpenGL on older devices), but Impeller on Android isn’t yet ready for preview. Android support is under active development, and we hope to share more about it — with more news about support for desktop and web — in future releases. + +Follow along with our progress in the [Impeller project board](https://github.com/orgs/flutter/projects/21) on GitHub. + +## iOS release validation + +When you release an iOS app, a [checklist of settings to update](https://docs.flutter.dev/deployment/ios#review-xcode-project-settings) ensures that your app is ready for submission to the App Store. + +The `flutter build ipa` command now validates some of these settings, and informs you if there are changes that should be made to your app before release. + +<DashImage figure src="images/0nMxlWBkYUzlFdnI2.webp" /> + + +## DevTools updates + +In this release, there are several new tooling features and overall improvements to try out. The DevTools Memory debugging tool has undergone a complete overhaul. There are three new feature tabs, **Profile**, **Trace**, and **Diff**, that support all the previously supported memory debugging features and added more for your debugging ease. New features include the ability to analyze the current memory allocation for your app by class and memory type, investigate what code paths are allocating memory for a set of classes at runtime, and diff memory snapshots to understand memory management between two points in time. + +<DashImage figure src="images/0i5K5F5bxN_5X0EAi.webp" /> + + +All of these new memory features have been documented on [docs.flutter.dev](https://docs.flutter.dev/development/tools/devtools/memory), so check out the documentation for more details. + +The Performance page also has a couple notable new features. A new **Frame Analysis** tab at the top of the Performance page provides insights for the selected Flutter frame. Insights might include suggestions on how to trace expensive parts of the Flutter frame in more detail, or warnings about expensive operations detected in the Flutter frame. + +<DashImage figure src="images/0op4bHcx7HvorMKSE.webp" /> + + +These are just a couple highlights, but this release contains several bug fixes and improvements beyond the features mentioned here, including some important bug fixes for the Inspector, the Network profiler, and the CPU profiler. For a more in-depth list of updates, check out the release notes for the DevTools changes that made it into Flutter 3.7. + +* [Flutter DevTools 2.17.0 release notes](https://docs.flutter.dev/development/tools/devtools/release-notes/release-notes-2.17.0) + +* [Flutter DevTools 2.18.0 release notes](https://docs.flutter.dev/development/tools/devtools/release-notes/release-notes-2.18.0) + +* [Flutter DevTools 2.19.0 release notes](https://docs.flutter.dev/development/tools/devtools/release-notes/release-notes-2.19.0) + +* [Flutter DevTools 2.20.0 release notes](https://docs.flutter.dev/development/tools/devtools/release-notes/release-notes-2.20.0) + +## Custom context menus + +You can now create custom context menus anywhere in a Flutter app. You can also use them to customize built-in context menus. + +For example, you could add a “Send email” button to the default text selection toolbar that shows up when the user has selected an email address ([code](https://github.com/flutter/samples/blob/main/experimental/context_menus/lib/email_button_page.dart)). See the [`contextMenuBuilder`](https://api.flutter.dev/flutter/cupertino/CupertinoTextField/contextMenuBuilder.html) parameter, which has been added to existing widgets that show a context menu by default, like `TextField`. You can return any widget you want from `contextMenuBuilder`, including modifying the default platform-adaptive context menu. + +<DashImage figure src="images/0dYBqWaVEAscMJ2Yz.webp" /> + + +This new feature works outside of text selection, too. You could, for example, create an `Image` widget that shows a **Save** button when right clicked or long pressed ([code](https://github.com/flutter/samples/blob/main/experimental/context_menus/lib/image_page.dart)). Use [`ContextMenuController`](https://api.flutter.dev/flutter/widgets/ContextMenuController-class.html) to display the current platform’s default context menu, or a custom one, anywhere in your app. + +<DashImage figure src="images/0uq8A_Zq5BM2DGDRT.webp" /> + + +See a full suite of examples in [Flutter’s samples repository](https://github.com/flutter/samples/tree/main/experimental/context_menus). + +## CupertinoListSection and CupertinoListTile widgets + +Thanks to the efforts of the Github user [Campovski](https://github.com/campovski), Cupertino has two new widgets, [`CupertinoListSection` and `CupertinoListTile`](https://github.com/flutter/flutter/pull/78732), for showing a scrollable list of widgets in the iOS style. They are the Cupertino versions of `ListView` and `ListTile` in Material. + +<DashImage figure src="images/07rMA1GAEoQxfkwYC.webp" /> + + +<DashImage figure src="images/0oLSR3-_9jQzCozYV.webp" /> + + +## Scrolling improvements + +Several [scrolling updates](https://github.com/flutter/flutter/issues?page=1&q=is%3Aissue+is%3Aclosed+closed%3A2022-07-11..2022-11-30+label%3A%22f%3A+scrolling%22+reason%3Acompleted) have arrived with this release: polish and refinement for trackpad interactions, new widgets like `Scrollbars` and `DraggableScrollableSheet`, and improved handling for text selection within scrolling contexts. + +Notably, MacOS apps will now experience higher fidelity with the addition of [new scrolling physics](https://github.com/flutter/flutter/pull/108298) to match the desktop platform. + +New [`AnimatedGrid`](https://github.com/flutter/flutter/pull/112982) and `SliverAnimatedGrid` widgets animate items added to (or removed from) a list. + +<DashImage figure src="images/0SY_JFBrqhg_Gs64Z.gif" /> + + +Lastly, we [fixed a regression](https://github.com/flutter/flutter/pull/108706) in the builder constructor of several scrolling widgets, like `ListView`. During the NNBD migration of the Flutter framework, the `itemBuilder`, which allows users to provide widgets on demand, was migrated to an `IndexedWidgetBuilder`. This meant that the `itemBuilder` could no longer return `null`, which (in the past) could be used to indicate that the end of the list had been reached. This functionality was restored with `NullableIndexedWidgetBuilder`. Thanks to @rrousselGit for noticing this — years after the migration — and sending a fix! + +## Internationalization tools and docs + +Internationalization support has been completely revamped! We’ve completely rewritten the `gen-l10n` tool to support: + +* Descriptive syntax errors. + +* Complex messages involving nested/multiple plurals, selects, and placeholders. + +<DashImage figure src="images/0VSZRJbLyqlfekARB.webp" /> + + +For more information, see the updated [Internationalizing Flutter apps](https://docs.flutter.dev/development/accessibility-and-localization/internationalization) page. + +## Global selection improvements + +`SelectionArea` now supports keyboard selections. You can extend an existing selection with keyboard shortcuts such as `shift+right`. + +<iframe src="https://vimeo.com/792060738" width="796" height="616" frameborder="0" allowfullscreen></iframe> + + +## Background isolates + +Now [Platform Channels](https://docs.flutter.dev/development/platform-integration/platform-channels) can be invoked from any [`Isolate`](https://docs.flutter.dev/development/packages-and-plugins/background-processes). Previously, users were only able to invoke Platform Channels from Flutter’s supplied main isolate. This makes working with isolates and host platform code in [Plugins](https://docs.flutter.dev/development/packages-and-plugins/developing-packages) or [Add-to-app](https://docs.flutter.dev/development/add-to-app) better. For more information, check out [Writing custom platform-specific code](https://docs.flutter.dev/development/platform-integration/platform-channels) on flutter.dev, and the in-depth article, [Introducing background isolate channels](https://medium.com/flutter/introducing-background-isolate-channels-7a299609cad8), a free article on Medium. + +## Text magnifier + +The magnifying glass that appears during text selection on Android and iOS now works in Flutter. This is enabled out of the box for all apps with text selection, but if you want to disable or customize it, see the [magnifierConfiguration](https://master-api.flutter.dev/flutter/material/TextField/magnifierConfiguration.html) property. + +<DashImage figure src="images/0vuIpoOEp5lqEZYeW.webp" /> + + +<DashImage figure src="images/0FIlFtTa6DAlXUwqE.webp" /> + + +## Swift migration for plugins + +With Apple focusing on Swift for their own APIs, we wanted to develop references to help Flutter plugin developers migrate or create new plugins with Swift. The [quick_actions](https://pub.dev/packages/quick_actions) plugin has been migrated from Objective-C to Swift, and can be used as a demonstration of best practices. If you are interested in helping us migrate 1P plugins, see [the Swift migration section](https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#swift-migration-for-1p-plugins) of the wiki. + +**Resources for iOS developers** + +We have published several new resources for iOS developers, including: + +* [Flutter for SwiftUI developers](https://docs.flutter.dev/get-started/flutter-for/ios-devs?tab=swiftui) + +* [Dart for Swift developers](https://dart.dev/guides/language/coming-from/swift-to-dart) + +* [Flutter concurrency for Swift Developers](https://docs.flutter.dev/resources/dart-swift-concurrency) + +* [Adding Flutter to an existing SwiftUI app](https://docs.flutter.dev/development/add-to-app/ios/add-flutter-screen) + +* [Creating flavors with Flutter](https://docs.flutter.dev/deployment/flavors) (for both Android and iOS) + +## Bitcode deprecation + +[Starting with Xcode 14, bitcode is no longer required for watchOS and tvOS applications, and the App Store no longer accepts bitcode submissions from Xcode 14.](https://developer.apple.com/documentation/xcode-release-notes/xcode-14-release-notes) As such, bitcode support has been removed from Flutter. + +By default, Flutter apps don’t have bitcode enabled, and we don’t expect this to impact many developers. However, if you have enabled bitcode manually in your Xcode project, disable it as soon as you upgrade to Xcode 14. You can do so by opening `ios/Runner.xcworkspace` and set **Enable Bitcode** to **No**. Add-to-app developers should disable it in the host Xcode project. + +<DashImage figure src="images/0OQtroLJAJcetxyeV.webp" /> + + +To learn more about bitcode distribution, Check out [Apple’s documentation](https://help.apple.com/xcode/mac/11.0/index.html?localePath=en.lproj#/devde46df08a). + +## iOS PlatformView BackdropFilter + +We’ve added the ability for native iOS views to be blurred when rendered underneath a blurred Flutter widget, and `UiKitView` widgets can now be wrapped inside a `BackdropFilter`. + +<DashImage figure src="images/0kdYw1nYkT8aKHjZP.webp" /> + + +For more information, see the [iOS PlatformView BackdropFilter](http://flutter.dev/go/ios-platformview-backdrop-filter-blur) design doc. + +## Memory management + +This release introduces a few improvements to memory management that have the collective effect of reducing jank caused by garbage collection pauses, reducing CPU utilization due to allocation velocity and background GC threads, and reducing the memory footprint. + +As one example, we have expanded the existing practice of manually deallocating native resources that back certain `dart:ui` Dart objects. Previously, the native resources would be held by the Flutter engine until the Dart VM garbage collected the Dart objects. Through the analysis of user applications and our own benchmarks, we determined that this strategy in general doesn’t do enough to avoid ill-timed GCs and overuses memory. Therefore, in this release, the Flutter engine adds API for explicitly deallocating the native resources held by `Vertices`, `Paragraph`, and `ImageShader` objects. + +<DashImage figure src="images/0LmVDzMTW34id5CNV.webp" /> + + +In our benchmarks of the Flutter framework migrated to this API, these improvements reduced 90%-ile frame build times up to more than 30%, which end-users will experience as smoother animations with less jank. + +Additionally, the Flutter engine [no longer registers](https://github.com/flutter/engine/pull/35473) the size of GPU images with the Dart VM. As above, these images were already manually deallocated by the framework when no longer needed, so informing Dart’s GC policies of the size of the GPU memory backing the Dart heap objects needlessly increased Dart heap memory pressure, triggering ill-timed GCs that could not have collected any additional memory. Along similar lines, it’s now the policy of the Flutter engine to report to the Dart VM [only the shallow size](https://github.com/flutter/engine/pull/35813) of the native objects that back `dart:ui` Dart objects. + +<DashImage figure src="images/0P_s-0gE5JGUerDkA.webp" /> + + +In our benchmarks, this change eliminates synchronous GC work while building frames when a widget creates GPU resident images. + +In this release, the Flutter Engine also does a better job of dynamically updating the Dart VM with information about Flutter application state. In particular, Flutter now uses the Dart VM’s [RAIL](https://web.dev/rail/) style [API](https://github.com/dart-lang/sdk/commit/c6a1eb1b61844b2d733f9e2f4c7754f1920325d7) to enter a [low-latency mode during route transition animations](https://github.com/flutter/flutter/pull/110600). During this low-latency mode, the Dart VM’s memory allocator prefers heap growth over garbage collection to avoid interrupting transition animations with GC pauses. While this change didn’t entail any dramatic performance improvement, we plan to expand usage of this model in future releases to further eliminate ill-timed GC pauses. Additionally, we have [fixed errors](https://github.com/flutter/engine/pull/37737) in the logic that decides when to notify the Dart VM that the Flutter engine is idle. Fixing these errors also prevents GC-related jank. Finally, for add-to-app Flutter applications, the Flutter engine now [informs the Dart VM](https://github.com/flutter/engine/pull/37539) when the Flutter view is no longer displayed. This now causes the Dart VM to trigger a final major GC for the `Isolate` associated with the view. This change reduces Flutter’s memory footprint when no Flutter views are visible. + +## Sunsetting macOS 10.11 through 10.13 + +As [previously announced](https://medium.com/flutter/whats-new-in-flutter-3-3-893c7b9af1ff#19cd), Flutter no longer supports macOS versions 10.11 and 10.12. Since that announcement, [further analysis](https://github.com/flutter/flutter/issues/114445) revealed that removing support for 10.13 as well would have limited additional impact, and would help the team to greatly simplify the codebase. This means that apps built against stable Flutter SDKs with this release and onward will no longer work on these versions, and the minimum macOS version supported by Flutter increases to 10.14 Mojave. + +As a consequence, since all versions of iOS and macOS supported by Flutter include Metal support, the OpenGL backend has been removed from both the iOS and macOS embedders. Removing these backends reduced the compressed size of the Flutter engine by about 100KB. + +## toImageSync + +This release [adds the methods](https://github.com/flutter/engine/pull/33736) `Picture.toImageSync` and `Scene.toImageSync` to `dart:ui`, analogous to the asynchronous methods `Picture.toImage`, and `Scene.toImage.Picture.toImageSync` synchronously returns a handle to an `Image` from a `Picture`, with the rasterization for the `Image` taking place asynchronously in the background. The image is then kept as GPU resident when a GPU context is available, meaning that it is faster to draw compared to images produced by `toImage`. (Images produced by `toImage` could also be kept GPU resident, but this optimization has not yet been implemented in that scenario.) + +The new `toImageSync` APIs support use-cases, such as: + +* Quickly snapping off an expensive-to-rasterize picture for reuse across multiple frames. + +* Applying multi-pass filters to a picture. + +* Applying custom shaders. + +As one example, the Flutter framework [now uses this API](https://github.com/flutter/flutter/pull/106621) to improve the performance of page transitions on Android, which nearly halves frame rasterization times, reduces jank, and allows the animation to hit 90/120fps on devices that support those refresh rates. + +## Custom shader support improvements + +This release includes numerous improvements to Flutter’s support for custom fragment shaders. The Flutter SDK now includes a shader compiler that compiles GLSL shaders listed in the `pubspec.yaml` file to the correct backend-specific format for the target platform. Additionally, custom shaders can now be hot reloaded for a convenient development cycle. Custom shaders are also now supported by both the Skia and Impeller backends on iOS. + +We are very impressed by the demos that the community has already shared, and are excited to see further innovative use of custom shaders in Flutter: + +[https://twitter.com/reNotANumber/status/1599717360096620544](https://twitter.com/reNotANumber/status/1599717360096620544) + +[https://twitter.com/reNotANumber/status/1599810391625719810](https://twitter.com/reNotANumber/status/1599810391625719810) + +[https://twitter.com/wolfenrain/status/1600242975937687553](https://twitter.com/wolfenrain/status/1600242975937687553) + +[https://twitter.com/iamjideguru/status/1598308434608283650](https://twitter.com/iamjideguru/status/1598308434608283650) + +[https://twitter.com/rxlabz/status/1609975128758026247](https://twitter.com/rxlabz/status/1609975128758026247) + +[https://twitter.com/RealDevOwl/status/1528357506795421698](https://twitter.com/RealDevOwl/status/1528357506795421698) + +[https://twitter.com/TakRutvik/status/1601380047599808513](https://twitter.com/TakRutvik/status/1601380047599808513) + +[https://twitter.com/wolfenrain/status/1600601043477401606](https://twitter.com/wolfenrain/status/1600601043477401606) + +See detailed documentation for [writing and using custom fragment shaders](https://docs.flutter.dev/development/ui/advanced/shaders) on docs.flutter.dev, and a helpful package of utilities, [`flutter_shaders`](https://pub.dev/packages/flutter_shaders) on pub.dev. + +## Font asset hot reload + +Previously, adding new fonts to the `pubspec.yaml` file required rebuilding the application to see them, unlike other asset types that could be hot reloaded. Now changes to the font manifest, including additions of new fonts, can be hot reloaded into an application. + +## Reduce animation jank on iOS devices + +Thanks to open source contributions from [luckysmg](https://github.com/luckysmg), two improvements have reduced animation jank on iOS. In particular, [the addition](https://github.com/flutter/engine/pull/35592) of a dummy `CADisplayLink` on the main thread during gestures now forces refreshes at the max refresh rate. Also, keyboard animations [now set](https://github.com/flutter/engine/pull/34871) the refresh rate of the `CADisplayLink` to the same refresh rate used by Flutter engine’s animator. Thanks to these changes, users should notice more consistently smooth animations on 120Hz iOS devices. + +## Summary + +It is an understatement to say that Flutter would not be the amazing experience that it is today without its community of talented and passionate contributors. As we continue this journey together, the Flutter team at Google wants you all to know that we could not do this without you. Thank you! + +The momentum isn’t slowing down, stay tuned for future updates! \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-3/images/0z8pzEtJOPFv-xzw2.webp b/sites/www/content/blog/whats-new-in-flutter-3/images/0z8pzEtJOPFv-xzw2.webp new file mode 100644 index 0000000000..6f1ee0a581 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3/images/0z8pzEtJOPFv-xzw2.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3/images/1K1Ru7PVkH74N56hgjBTjjQ.webp b/sites/www/content/blog/whats-new-in-flutter-3/images/1K1Ru7PVkH74N56hgjBTjjQ.webp new file mode 100644 index 0000000000..b649c10f9d Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3/images/1K1Ru7PVkH74N56hgjBTjjQ.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3/images/1kS32jfapJAvSyspT3aOH5A.webp b/sites/www/content/blog/whats-new-in-flutter-3/images/1kS32jfapJAvSyspT3aOH5A.webp new file mode 100644 index 0000000000..57642bb648 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-3/images/1kS32jfapJAvSyspT3aOH5A.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-3/index.md b/sites/www/content/blog/whats-new-in-flutter-3/index.md new file mode 100644 index 0000000000..1e7f24ff63 --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-3/index.md @@ -0,0 +1,177 @@ +--- +title: "What’s new in Flutter 3" +description: "Deep dive into our latest release, including macOS and Linux stable, performance improvements, and more!" +publishDate: 2022-05-11 +author: itsjustkevin +image: images/1K1Ru7PVkH74N56hgjBTjjQ.webp +category: releases +layout: blog +--- + +It’s time again for a Flutter stable release — and we are incredibly proud to announce **Flutter 3**! Only three months ago, we announced Flutter support for Windows. Today, we are excited to announce that Flutter is now stable for macOS and Linux, in addition to Windows! + +<DashImage figure src="images/1K1Ru7PVkH74N56hgjBTjjQ.webp" /> + + +Thanks to the hard work of our Flutter contributors, we’ve merged 5248 pull requests! + +We have several exciting things to announce as part of this release, including the update on Flutter’s support for macOS and Linux, significant performance improvements, mobile and web updates — and much more! In addition, we have news about reduction in support for older versions of Windows, and a short list of breaking changes. So, let’s get down to business! + +## Ready for production on all desktop platforms + +Linux and macOS have reached stable and include the following features: + +### Cascading menus and support for the macOS system menu bar + +You can now create platform-rendered menu bars on macOS using the `PlatformMenuBar` widget, which supports insertion of platform-only menus, and control over what appears in the macOS application menus. + +<DashImage figure src="images/1kS32jfapJAvSyspT3aOH5A.webp" alt="Cascading menus demo" caption="Cascading menus demo" /> + + +### Full support for international text input on all desktop platforms + +International text input, including for languages that make use of text [input method editors](https://en.wikipedia.org/wiki/Input_method) (IMEs) such as Chinese, Japanese, and Korean is fully-supported on all three desktop platforms, including third-party input methods such as Sogou and Google Japanese Input. + +### Accessibility on all desktop platforms + +Flutter for Windows, macOS, and Linux support accessibility services such as screen-readers, accessible navigation, and inverted colors. + +### Universal binaries by default on macOS + +As of Flutter 3, Flutter macOS desktop apps are built as universal binaries, with native support for both existing Intel-based Macs and Apple’s latest Apple Silicon devices. + +### Deprecating Windows 7/8 for development + +With this release, we’re raising the recommended Windows version for development to Windows 10. While we aren’t blocking development on older versions (Windows 7, Windows 8, Windows 8.1), these versions are [no longer supported by Microsoft](https://docs.microsoft.com/en-us/lifecycle/faq/windows) and we provide limited testing on these releases. While we will continue to offer ‘best effort’ support for older versions, we encourage you to upgrade. +> **Note**: We continue to offer support for Flutter apps *running* on Windows 7 and Windows 8; this change only affects the recommended development environment. + +## Mobile updates + +Our updates to mobile platforms includes the following: + +### Foldable phone support + +The Flutter 3 release supports foldable mobile devices. In a collaboration spearheaded by Microsoft, new features and widgets allow you to create dynamic and delightful experiences on foldable devices. + +As part of this work, [`MediaQuery`](https://master-api.flutter.dev/flutter/widgets/MediaQuery-class.html) now contains a list of [`DisplayFeatures`](https://master-api.flutter.dev/flutter/dart-ui/DisplayFeature-class.html), describing the bounds and states of device elements like hinges, folds, and cutouts. Additionally, the [`DisplayFeatureSubScreen`](https://master-api.flutter.dev/flutter/widgets/DisplayFeatureSubScreen-class.html) widget now positions its child widget without overlapping the bounds of `DisplayFeatures`, and has already been integrated with the framework’s default dialogs and pop-ups, making Flutter aware and responsive to these elements **out of the box**. + +<DashImage figure src="images/0z8pzEtJOPFv-xzw2.webp" /> + + +Many thanks goes to the Microsoft team, and especially to [@andreidiaconu](https://github.com/andreidiaconu), for their contributions! + +Try [the Surface Duo emulator samples](https://docs.microsoft.com/en-us/dual-screen/flutter/samples), including one with a special fork of the Flutter Gallery, to see Flutter’s dual displays in action. + +### iOS variable refresh rate support + +Flutter now supports variable refresh rate on iOS devices with ProMotion displays, including iPhone 13 Pro and iPad Pro. On these devices, Flutter apps can render at refresh rates reaching 120 hz, which were previously limited to 60 hz. This results in a smoother experience during fast animations such as scrolling. See [flutter.dev/go/variable-refresh-rate](http://flutter.dev/go/variable-refresh-rate) for more details. + +### Simplified iOS releases + +We’ve added [new options to the flutter build ipa command](https://github.com/flutter/flutter/pull/97672) to simplify releasing your iOS app. When you’re ready to distribute to TestFlight or the App Store, run `flutter build ipa` to build an Xcode archive (`.xcarchive` file) and an app bundle (`.ipa` file). You can optionally add `—-export-method ad-hoc`, `—-export-method development`, or `—-export-method enterprise`. Once the app bundle is built, upload it to Apple through the [Apple Transport macOS app](https://apps.apple.com/us/app/transporter/id1450874784) or on the command line using `xcrun altool` (run `man altool` for App Store Connect API key authentication instructions). After uploading, your app is available for release to [TestFlight or the App Store](https://docs.flutter.dev/deployment/ios#release-your-app-to-the-app-store). After setting up your initial [Xcode project settings](https://docs.flutter.dev/deployment/ios#review-xcode-project-settings), such as display name and app icon, you no longer need to open Xcode to release your app. + +### Gradle version update + +If you create a new project with the Flutter tool, you might notice that the generated files now use the latest versions of the Gradle and Android Gradle plugins. For existing projects, you need to manually bump the versions to 7.4 for Gradle, and 7.1.2 for the Android Gradle plugin. + +### Sunsetting 32-bit iOS/iOS 9/iOS 10 + +As we announced in Feb 2022 with the 2.10 stable release, Flutter’s support for 32-bit iOS devices and iOS versions 9 and 10 is coming to an end. This change affects the iPhone 4S, iPhone 5, iPhone 5C, and the 2nd, 3d, and 4th gen iPad devices. Flutter 3 is the last stable release that supports these iOS versions and devices. + +To learn more about this change, take a look at [RFC: End of support for 32-bit iOS devices](http://flutter.dev/go/rfc-32-bit-ios-unsupported). + +## Web updates + +Our updates for web apps include the following: + +### Image decoding + +Flutter web now automatically detects and uses the ImageDecoder API in browsers that support it. As of today, most Chromium-based browsers (Chrome, Edge, Opera, Samsung Browser, and more) have added this API. + +The new API decodes images asynchronously off the main thread using the browser’s built-in image codecs. This speeds up image decoding by 2x, and it never blocks the main thread, removing all jank that was previously caused by images. + +### Web app lifecycles + +The new lifecycle API for Flutter web apps gives you the flexibility to control the bootstrap process of your Flutter app from the hosting HTML page, and helps Lighthouse analyze the performance of your app. This applies to many use-cases, including the following frequently requested scenarios: + +* A splash screen. + +* A loading indicator. + +* A plain HTML interactive landing page displayed before the Flutter app. + +For more information, check out [Customizing web app initialization](https://docs.flutter.dev/development/platform-integration/web/initialization) on docs.flutter.dev. + +## Tooling updates + +Our updates to Flutter and Dart tooling include: + +### Updated lint package + +Version 2.0 of the lint packages have been released: + +* Flutter: [https://pub.dev/packages/flutter_lints/versions/2.0.0](https://pub.dev/packages/flutter_lints/versions/2.0.0) + +* Dart: [https://pub.dev/packages/lints/versions/2.0.0](https://pub.dev/packages/lints/versions/2.0.0) + +Apps generated in Flutter 3 with `flutter create` automatically enable the v2.0 sets of lints. Existing apps, packages, and plugins are encouraged to migrate to v2.0 to follow the latest and greatest best practices in the Flutter world, by running `flutter pub upgrade --major-versions flutter_lints`. + +Most of the newly added lint warnings in v2 come with automated fixes. So, after upgrading to the latest package version in your app’s `pubspec.yaml` file, you can run `dart fix —-apply` over your code base to fix most lint warnings automatically (some warnings still require some manual work). Apps, packages, or plugins that aren’t using `package:flutter_lints` yet can migrate by following the [migration guide](https://docs.flutter.dev/release/breaking-changes/flutter-lints-package#migration-guide). + +### Performance improvements + +Thanks to open source contributor [knopp](https://github.com/knopp), partial repaint has been [enabled on Android](https://github.com/flutter/engine/pull/29591) devices that support it. In our local testing this change cut average, 90th percentile, and 99th percentile frame rasterization times on the `backdrop_filter_perf` benchmark on a Pixel 4 XL device by 5x. Partial repaint when there is a single rectangular dirty region is now enabled on both iOS and newer Android devices. + +We have [further improved](https://github.com/flutter/engine/pull/30957) the performance of opacity animations in simple cases. In particular, when an `Opacity` widget contains only a single rendering primitive, the `saveLayer` method that is usually invoked by `Opacity` is omitted. In a benchmark constructed to measure the benefits of this optimization, rasterization time for this case improved by [an order of magnitude](https://flutter-flutter-perf.skia.org/e/?begin=1643063115&end=1644004520&keys=X32827d8819e8271e025f50e77bf2bec0&requestType=0&xbaroffset=27447). In future releases we plan to apply this optimization to even more scenarios. + +Thanks to work by open source contributor [JsouLiang](https://github.com/JsouLiang), the Engine’s raster and UI threads now run at higher priority on Android and iOS than other threads; for example, Dart VM background garbage collection threads. In our benchmarks, this resulted in [~20% faster](https://flutter-flutter-perf.skia.org/e/?begin=1644581114&end=1644647407&keys=X3999dc0a0c89054eaa9f66bcff27d882&num_commits=50&request_type=1&xbaroffset=27549) average frame build times. + +Prior to the 3 release, the admission policy for the raster cache only looked at the number of draw operations in a picture, assuming that any with more than a few operations would be good candidates for caching. Unfortunately, this resulted in the Engine spending memory to cache pictures that were actually very fast to render. This release [introduces a mechanism](https://github.com/flutter/engine/pull/31417) that estimates the rendering complexity of pictures based on the cost of draw operations that it contains. Using this as the raster cache admissions policy [reduced memory usage](https://flutter-flutter-perf.skia.org/e/?begin=1644790212&end=1646044276&keys=X4c7dd4e4903a38523816c00b31d4d787&requestType=0&xbaroffset=27636) without regressing performance in our benchmarks. + +Thanks to open source contributor [ColdPaleLight](https://github.com/ColdPaleLight) who [fixed a bug in frame scheduling](https://github.com/flutter/engine/pull/31513) that caused a small number of animation frames to be dropped on iOS. Thanks to everyone who reported this issue and provided reproductions and videos of the dropped frames. + +### Impeller + +The team has been hard at work on a solution to address early-onset jank on iOS and other platforms. In the Flutter 3 release, you can preview an experimental rendering backend called [Impeller](https://github.com/flutter/engine/tree/main/impeller) on iOS. Impeller precompiles [a smaller, simpler set of shaders](https://github.com/flutter/flutter/issues/77412) at engine build time so that they won’t compile while an app is running; this has been a major source of jank in Flutter. Impeller is not production ready and is far from finished. Not all Flutter features are implemented yet, but we’re pleased enough with its fidelity and performance in the [flutter/gallery](https://github.com/flutter/gallery) app that we are sharing our progress. In particular, the worst frame in the transition animation of the Gallery app is around [20x faster](https://flutter-flutter-perf.skia.org/e/?begin=1650297849&end=1651261748&queries=sub_result%3Dworst_frame_rasterizer_time_millis%26test%3Dnew_gallery_impeller_ios__transition_perf%26test%3Dnew_gallery_ios__transition_perf&requestType=0). + +Impeller is available under a flag on iOS. You can pass `—-enable-impeller` to `flutter run`, or set the `FLTEnableImpeller` flag in your `Info.plist` file to `true`, to give Impeller a try. Development of Impeller continues on Flutter’s master channel, and we hope to provide further updates in future releases. + +### Inline ads on android + +When you use the `[google_mobile](https://pub.dev/packages/google_mobile_ads)_ads` package, you should see better performance in user critical interactions such as scrolling and transitions between pages. This is particularly noticeable on devices popular in emerging markets. The best part is that no code changes are required! + +Under the hood, Flutter now composes Android views, commonly referred to as [platform views](https://docs.flutter.dev/development/platform-integration/platform-views), asynchronously. This means that the Flutter raster thread doesn’t need to wait for the Android view to render. Instead, the Flutter engine places the view on screen using an OpenGL texture that it manages. + +## More exciting updates + +Other updates to the Flutter ecosystem include the following: + +### Material 3 + +Flutter 3 supports [Material Design 3](https://m3.material.io/), the next generation of Material Design. Flutter 3 provides opt-in support for Material 3; this includes Material You features like dynamic color, an updated color system and typography, updates to many components, and new visual effects introduced in Android 12 like a new touch ripple design and a stretch overscroll effect. Try the Material 3 features in the *new* [Take your Flutter app from Boring to Beautiful](https://codelabs.developers.google.com/codelabs/flutter-boring-to-beautiful) codelab. See the [API docs](https://api.flutter.dev/flutter/material/ThemeData/useMaterial3.html) for details on how to opt-in to these new features and what components have support for Material 3. Follow ongoing work with the [Material 3 Umbrella issue](https://github.com/flutter/flutter/issues/91605). + +### Theme extensions + +Flutter now enables adding *anything* to the material library’s `ThemeData`, with a concept called *Theme extensions*. Rather than extending (in the Dart sense) `ThemeData` and re-implementing its `copyWith`, `lerp`, and other methods, you can specify `ThemeData`.extensions. Also, as a package developer, you can provide `ThemeExtension`. See [flutter.dev/go/theme-extensions](https://flutter.dev/go/custom-colors-m3) for more details and check out [this example on GitHub](https://github.com/guidezpl/flutter/blob/master/examples/api/lib/material/theme/theme_extension.1.dart). + +### Ads + +We know it is important for publishers to request consent for personalized ads and to handle Apple’s App Tracking Transparency (ATT) requirements. + +To support these requirements, Google offers the User Messaging Platform (UMP) SDK, which replaces the previous open source [Consent SDK](https://github.com/googleads/googleads-consent-sdk-ios). In the upcoming release of GMA SDK for Flutter, we are adding support for the UMP SDK to allow publishers to obtain user consent. For more details, check out the [google_mobile_ads](https://pub.dev/packages/google_mobile_ads) page on pub.dev. + +## Breaking changes + +As we continue to grow and improve Flutter, we aim to keep the number of breaking changes to a minimum. With the release of Flutter 3, we have the following breaking changes: + +* [Deprecated API removed after v2.10](https://docs.flutter.dev/release/breaking-changes/2-10-deprecations) + +* [Page transitions replaced by ZoomPageTransitionsBuilder](https://docs.flutter.dev/release/breaking-changes/page-transition-replaced-by-ZoomPageTransitionBuilder) + +* [Migrate useDeleteButtonTooltip to deleteButtonTooltipMessage of Chips](https://docs.flutter.dev/release/breaking-changes/chip-usedeletebuttontooltip-migration) + +If you are using any of these apis please refer to the [migration guide on Flutter.dev](https://docs.flutter.dev/release/breaking-changes). + +## Summary + +From the Flutter Team at Google, we say thank you for the awesome work the community has done to help Flutter maintain its status as the most popular cross-platform UI toolkit, as measured by analysts like Statista and SlashData. We look forward to working together as a community to continue to provide a community driven tool that helps create a delightful experience for the developers and users alike! \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-in-flutter-beta-3/images/0Sdp8z8g2G2spsW3j.webp b/sites/www/content/blog/whats-new-in-flutter-beta-3/images/0Sdp8z8g2G2spsW3j.webp new file mode 100644 index 0000000000..10d8b52d92 Binary files /dev/null and b/sites/www/content/blog/whats-new-in-flutter-beta-3/images/0Sdp8z8g2G2spsW3j.webp differ diff --git a/sites/www/content/blog/whats-new-in-flutter-beta-3/index.md b/sites/www/content/blog/whats-new-in-flutter-beta-3/index.md new file mode 100644 index 0000000000..08ec4a8068 --- /dev/null +++ b/sites/www/content/blog/whats-new-in-flutter-beta-3/index.md @@ -0,0 +1,124 @@ +--- +title: "What’s New in Flutter Beta 3" +description: "This week marks the release of the third beta for Flutter, our toolkit for building beautiful mobile UI for iOS and Android. In our…" +publishDate: 2018-05-07 +author: timsneath +image: images/0Sdp8z8g2G2spsW3j.webp +category: releases +layout: blog +--- + +This week marks the release of the third beta for Flutter, our toolkit for building beautiful mobile UI for iOS and Android. In [our announcement over at the Google Developer site](https://developers.googleblog.com/2018/05/ready-for-production-apps-flutter-beta-3.html), we provide a broader overview of the Flutter project and showcase some examples of how customers are using it to create amazing applications. In this blog post, we’ll take a deeper look at the specific improvements we’ve made in Beta 3 itself. + +As mentioned when we shipped [our first beta at Mobile World Congress](https://medium.com/flutter-io/announcing-flutter-beta-1-build-beautiful-native-apps-dc142aea74c0) in February, our intent is to continue to ship releases that are at least of beta quality on approximately a monthly cadence. This release (v0.3.2) is the third release in that lineage, and demonstrates continued progress towards completing the 1.0 release. + +Our work in this release has focused on three primary areas: fundamentals, ecosystem and tooling. + +### Fundamentals + +Let’s start with the fundamentals, where we’ve improved the built-in UI widgets, completed the remaining feature work for Dart 2, and introduced a new embedding API. + +In Flutter Beta 3, we’ve made a number of improvements to the **Material Design widgets** to enable greater flexibility and customization. For example: + +* We added a [BottomAppBar](https://docs.flutter.io/flutter/material/BottomAppBar-class.html) component; + +* Support for [Chips](https://docs.flutter.io/flutter/material/Chip-class.html) has been greatly improved and expanded; + +* [InputDecorator](https://docs.flutter.io/flutter/material/InputDecorator-class.html) now supports filled/underlined and outlined modes; + +* [FloatingActionButton](https://docs.flutter.io/flutter/material/FloatingActionButton-class.html) now offers greater positioning flexibility; + +* [Slider](https://docs.flutter.io/flutter/material/Slider-class.html) now offers customization of colors and shapes of the thumb and value indicators. + +Additionally, we’ve added support for animated resources in formats like GIF and WebP, ready for your best meme apps to be published to the app stores! + +We also made a number of updates to the Flutter Gallery application to demonstrate these and other changes. For example: + +* We’ve [updated the text fields d](https://github.com/flutter/flutter/pull/15362)emo to demonstrate the new InputDecorator features; + +* We’ve added a new [Backdrop demo](https://github.com/flutter/flutter/pull/15579/); + +* We’ve added demos for [OutlineButton](https://docs.flutter.io/flutter/material/OutlineButton-class.html) and the new Checkbox [tristate property](https://docs.flutter.io/flutter/material/Checkbox/tristate.html); + +* We’ve added custom [selection indicators](https://github.com/flutter/flutter/pull/15323) to the scrollable tabs demo. + +We’ve completed the work to enable **Dart 2**, [our reboot of the Dart language that is optimized for client development](https://medium.com/@asandholm/announcing-dart-2-80ba01f43b6). As of this release, Dart 2 is feature complete and enabled by default. + +In this release, Dart 2 adds some syntactic sugar to help with instantiating widgets in Flutter. The *new* keyword is now entirely optional: there should be no need to use it any longer, and it’s a bug if the compiler complains. A *build()* method like this is easier than ever to write: + +```dart +Widget build(BuildContext context) => Scaffold( + appBar: AppBar( + title: Text(widget.title), + actions: <Widget>[ + IconButton( + icon: Icon(Icons.info), + onPressed: _aboutAction, + ), + IconButton( + icon: Icon(Icons.share), + onPressed: _shareAction, + ) + ], + ), + body: Center( + child: _body(), + ), + floatingActionButton: FloatingActionButton( + onPressed: _refresh, + tooltip: 'Refresh', + child: Icon(Icons.refresh), + ), +); +``` + +The *const* keyword becomes optional for any child constructors within an existing *const* scope. During the first two betas, we identified scenarios where it was difficult to infer from the context whether an object’s children are immutable, so we require the *const* keyword at the top level to make the constant nature of the code explicit. Once you’ve declared a section as *const*, however, the children are automatically *const* without requiring further declaration, so now a statement like the following is valid: + +```dart +const comments = <Comment>[ + Comment( + "Creating apps is just faster and more fun with Flutter.", + "Posse Inc."), + Comment( + "Yesterday I was trying #Flutter for the first time, today I published an application.", + "@CristianDudca"), + Comment( + "This weekend: Met and fell in love with Flutter.", + "@FIREYOSE"), +]; +``` + +In other areas, we’ve made improvements to the accessibility support for apps that use Flutter, including improving support for screen readers, large text and contrast capabilities, as well as starting to [document our accessibility support](https://flutter.io/accessibility/). We’re also ready for apps that are offered in languages with right-to-left scripts. In addition to supporting right-to-left text, controls mirror where appropriate (for example the left ‘back’ button has inverted direction and justification in languages like Arabic). + +We’ve also [rewritten the Flutter engine’s threading model](https://github.com/flutter/engine/pull/4932) to make it possible to host multiple FlutterViews within a single application. This is part of our larger focus on making it easier to add Flutter to your existing app — work that continues [in progress](https://github.com/flutter/flutter/issues/14821). + +### Ecosystem + +We launched an initial suite of Firebase plugins at Google I/O last year. Several of those plugins are reaching their 1.0 milestone this week: [Realtime Database](https://pub.dartlang.org/packages/firebase_database), [Firebase Analytics](https://pub.dartlang.org/packages/firebase_analytics), [Firebase Messaging](https://pub.dartlang.org/packages/firebase_messaging), and [Firebase Core](https://pub.dartlang.org/packages/firebase_core). In addition, we have added new, fully-featured plugins for [Remote Config](https://pub.dartlang.org/packages/firebase_remote_config), [Cloud Firestore](https://pub.dartlang.org/packages/cloud_firestore), and [Performance Monitoring](https://pub.dartlang.org/packages/firebase_performance). For an overview of our Firebase support, please see the [FlutterFire page](https://github.com/flutter/plugins/blob/master/FlutterFire.md). + +Our support for ads powered by [AdMob by Google](https://www.google.com/admob/) is graduating to beta, enabling you to monetize your Flutter-based applications. The [AdMob plugin](https://pub.dartlang.org/packages/firebase_admob) supports loading and displaying banner, interstitial (full-screen), and rewarded video ads using the [AdMob API](https://firebase.google.com/docs/admob/). + +There are many other packages that have recently been made available for Flutter, some [contributed by the Flutter team](https://pub.dartlang.org/packages?q=email%3Aflutter-dev%40googlegroups.com) directly, others by community members. As mentioned in our I/O announcement post, Flutter is a first-class toolkit for [Material](http://material.io/components), which means the Material and Flutter teams have partnered to deliver even more support for Material Design. + +### Tooling + +We continue to release regular updates to the **Flutter plugin for Android Studio and IntelliJ** to improve the development experience. In particular, we’ve [redesigned the UI Inspector](https://groups.google.com/forum/#!topic/flutter-dev/N6nOno1m3Mk) with a new “Just My Widgets” feature that filters out auto-generated widgets. You can also run your Flutter app in profile mode, which adds frames-per-second and memory usage displays. + +**Visual Studio Code is now considered a [fully-supported development environment](https://flutter.io/get-started/editor/#vscode)** for Flutter, in addition to the Android Studio support mentioned above. Flutter support is enabled through the [Flutter](https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter) extension, available through the Visual Studio Marketplace. The latest version of our Visual Studio Code extension supports Flutter Beta 3 features including Dart 2, and the changelog is [here](https://dartcode.org/releases/v2-12/). + +Other notable features in this release for both Android Studio and Visual Studio Code include a broader set of refactorings. This includes an Extract Widget refactor that creates a new Widget class and inserts a call to its constructor in the original position: + +<DashImage figure src="images/0Sdp8z8g2G2spsW3j.webp" /> + + +### Connecting with us + +There are a few ways to get in touch with us and find out what’s going on with Flutter. + +* We post announcements and have discussions in the [flutter-dev](https://groups.google.com/d/forum/flutter-dev) group, and are also active on Twitter as @[flutterio](https://twitter.com/flutterio); + +* The best place to post technical questions is [Stack Overflow](https://stackoverflow.com/questions/tagged/flutter), where there are plenty of experts on standby to help, including Google engineers; + +* If you spot a bug that you think we need to take a look at, you can file it at our [GitHub repo](https://github.com/flutter/flutter/issues). This is also a good place to [upvote other suggestions](https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc) that you think we should prioritize; + +* Lastly, if you have a Flutter app that you’d like to show us, we’d love to see it! We’re always looking for new demos and [cool examples to share with others](https://flutter.io/showcase/). [Use this form](https://docs.google.com/forms/d/e/1FAIpQLScP5iDNGrlVXdGxmRDzjCnqXS0KUDld-3cR-njAL0kDO2ULFg/viewform) to tell us what you’re working on! \ No newline at end of file diff --git a/sites/www/content/blog/whats-new-with-the-slider-widget/images/0DEYZqsMMIdQWZ8mF.webp b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0DEYZqsMMIdQWZ8mF.webp new file mode 100644 index 0000000000..d90a6d5096 Binary files /dev/null and b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0DEYZqsMMIdQWZ8mF.webp differ diff --git a/sites/www/content/blog/whats-new-with-the-slider-widget/images/0HviG9DqaeW84PJi5.webp b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0HviG9DqaeW84PJi5.webp new file mode 100644 index 0000000000..8ae526d7ff Binary files /dev/null and b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0HviG9DqaeW84PJi5.webp differ diff --git a/sites/www/content/blog/whats-new-with-the-slider-widget/images/0LNKN4lwqpBxQZ60A.webp b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0LNKN4lwqpBxQZ60A.webp new file mode 100644 index 0000000000..7f87d76b3b Binary files /dev/null and b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0LNKN4lwqpBxQZ60A.webp differ diff --git a/sites/www/content/blog/whats-new-with-the-slider-widget/images/0PLNNQjK0_zOXok1A.webp b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0PLNNQjK0_zOXok1A.webp new file mode 100644 index 0000000000..42be684eca Binary files /dev/null and b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0PLNNQjK0_zOXok1A.webp differ diff --git a/sites/www/content/blog/whats-new-with-the-slider-widget/images/0dUrIA4k2wFu48rDe.webp b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0dUrIA4k2wFu48rDe.webp new file mode 100644 index 0000000000..a27160a8e5 Binary files /dev/null and b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0dUrIA4k2wFu48rDe.webp differ diff --git a/sites/www/content/blog/whats-new-with-the-slider-widget/images/0elEXE_pM0iEDuv_t.webp b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0elEXE_pM0iEDuv_t.webp new file mode 100644 index 0000000000..98469f518c Binary files /dev/null and b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0elEXE_pM0iEDuv_t.webp differ diff --git a/sites/www/content/blog/whats-new-with-the-slider-widget/images/0f7ar1ek7C7pUx5cF.webp b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0f7ar1ek7C7pUx5cF.webp new file mode 100644 index 0000000000..fe3936ade1 Binary files /dev/null and b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0f7ar1ek7C7pUx5cF.webp differ diff --git a/sites/www/content/blog/whats-new-with-the-slider-widget/images/0i3IWhVNFBB5OJEze.webp b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0i3IWhVNFBB5OJEze.webp new file mode 100644 index 0000000000..ca9e93471a Binary files /dev/null and b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0i3IWhVNFBB5OJEze.webp differ diff --git a/sites/www/content/blog/whats-new-with-the-slider-widget/images/0j7DxUmBmRK7Ztd5A.webp b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0j7DxUmBmRK7Ztd5A.webp new file mode 100644 index 0000000000..bedce96a31 Binary files /dev/null and b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0j7DxUmBmRK7Ztd5A.webp differ diff --git a/sites/www/content/blog/whats-new-with-the-slider-widget/images/0neQ_0e28CgjA00W_.webp b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0neQ_0e28CgjA00W_.webp new file mode 100644 index 0000000000..fc27aefd3f Binary files /dev/null and b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0neQ_0e28CgjA00W_.webp differ diff --git a/sites/www/content/blog/whats-new-with-the-slider-widget/images/0q81pqrMZMWkCmRLF.webp b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0q81pqrMZMWkCmRLF.webp new file mode 100644 index 0000000000..c62e48e951 Binary files /dev/null and b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0q81pqrMZMWkCmRLF.webp differ diff --git a/sites/www/content/blog/whats-new-with-the-slider-widget/images/0qIoICKw6wo7NcwOW.webp b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0qIoICKw6wo7NcwOW.webp new file mode 100644 index 0000000000..deaee16214 Binary files /dev/null and b/sites/www/content/blog/whats-new-with-the-slider-widget/images/0qIoICKw6wo7NcwOW.webp differ diff --git a/sites/www/content/blog/whats-new-with-the-slider-widget/images/10vbyZT-NITTvqljO8HtU4w.webp b/sites/www/content/blog/whats-new-with-the-slider-widget/images/10vbyZT-NITTvqljO8HtU4w.webp new file mode 100644 index 0000000000..e952d1f8af Binary files /dev/null and b/sites/www/content/blog/whats-new-with-the-slider-widget/images/10vbyZT-NITTvqljO8HtU4w.webp differ diff --git a/sites/www/content/blog/whats-new-with-the-slider-widget/index.md b/sites/www/content/blog/whats-new-with-the-slider-widget/index.md new file mode 100644 index 0000000000..5afb3d5b87 --- /dev/null +++ b/sites/www/content/blog/whats-new-with-the-slider-widget/index.md @@ -0,0 +1,190 @@ +--- +title: "What’s new with the Slider widget?" +description: "Flutter lets you create beautiful, natively compiled applications. The reason Flutter can do this is because Flutter loves Material…" +publishDate: 2020-06-18 +author: josealba +image: images/10vbyZT-NITTvqljO8HtU4w.webp +category: announcements +layout: blog +--- + +<DashImage figure src="images/10vbyZT-NITTvqljO8HtU4w.webp" /> + + +Flutter lets you create beautiful, natively compiled applications. The reason Flutter can do this is because Flutter loves Material. [Material](https://material.io) is a design system that helps build high-quality, digital experiences. As UI design continues to evolve, Material continues to update its components, motion, and design system. + +Flutter recently updated the [`Slider`](https://api.flutter.dev/flutter/material/Slider-class.html) and [`RangeSlider`](https://api.flutter.dev/flutter/material/RangeSlider-class.html) widgets to the latest the Material guidelines. The new sliders were designed with better accessibility in mind: The tracks are taller, the thumbs have shadows, and the value indicators have a new shape and improved text scaling support. + +This article describes the changes to the `Slider` and `RangeSlider` widgets. + +<DashImage figure src="images/0neQ_0e28CgjA00W_.webp" /> + + +## What’s new? + +`Slider` and `RangeSlider` were updated, to have a smoother experience when you work with these widgets. This diagram demonstrates the five different components that go into making the slider widgets. + +<DashImage figure src="images/0elEXE_pM0iEDuv_t.webp" /> + + +Both `Slider` and `RangeSlider` consist of five parts: + +1. Value indicators that show labels for thumb values + +1. A track that the thumbs slide across + +1. Thumbs that indicate the position of the value + +1. Overlays that display when the thumbs are pressed + +1. Tick marks on the track when the slider is discrete + +Note: This article refers to two different types of overlays: + +* The `OverlaySlider` component represents the component mentioned in the preceding image. + +* Flutter’s `Overlay` class is used to ‘float’ widgets over all the other widgets shown on the following screen. + +## New default value indicator shape + +The biggest visual difference between the old slider and the new slider widgets is the default value indicators: [`RectangularSliderValueIndicatorShape`](https://master-api.flutter.dev/flutter/material/RectangularSliderValueIndicatorShape-class.html) for `Slider` and [`RectangularRangeSliderValueIndicatorShape`](https://master-api.flutter.dev/flutter/material/RectangularRangeSliderValueIndicatorShape-class.html) for `RangeSlider`. The indicators reflect the latest design changes from Material. The old indicator shape was an upside-down pear, and the new shape is more rectangular and looks like a speech bubble. The value indicator was updated for both `Slider` and `RangeSlider`. + +<DashImage figure src="images/0i3IWhVNFBB5OJEze.webp" /> + + +## Value indicator paints on the overlay + +The updated value indicator is now painted on the overlay. This means that the indicator is no longer restricted to the scope of the slider’s `MediaQuery` and can be displayed over other widgets. This is expected because the indicator is only activated when dealing with the gesture detector, which means that it’s only shown when the user interacts with the slider. By integrating the [`Overlay`](https://api.flutter.dev/flutter/widgets/Overlay-class.html) with the [`CompositedTransformTarget`](https://api.flutter.dev/flutter/widgets/CompositedTransformTarget-class.html), [`CompositedTransformFollower`](https://api.flutter.dev/flutter/widgets/CompositedTransformFollower-class.html), and [`Layerlink`](https://api.flutter.dev/flutter/rendering/LayerLink-class.html) widgets, the value indicator can display over other widgets. To learn more about this mechanism, see [How to float an overlay widget over a (possibly transformed) UI widget](https://medium.com/flutter/how-to-float-an-overlay-widget-over-a-possibly-transformed-ui-widget-1d15ca7667b6). + +The following diagram shows how the `Slider` and `RangeSlider` widgets are made: + +<DashImage figure src="images/0DEYZqsMMIdQWZ8mF.webp" /> + + +You may notice that we use a separate `LeafRenderObjectWidget` to paint the indicators on the overlay. This is because we need to paint on both the `Overlay` class and the screen, which you can’t do from the same `LeafRenderObjectWidget` widget. + +Because we’re now painting the value indicator on the overlay, it’s no longer restricted to the scope of the `MediaQuery` that contains the slider. The indicator is no longer clipped to that box, which means that the entire value indicator is displayed always. + +<DashImage figure src="images/0HviG9DqaeW84PJi5.webp" /> + + +Note: This example was made with the `textScaleFactor` set to 4 for better demonstration. + +The value indicator rectangle is shifted so that part of the indicator doesn’t go off screen. This improvement allows you to increase the number of digits within the indicator and increase the text scale factor of your application without worrying if the text exceeds the screen size. In the following image, the value indicator text scale is 4 and contains a 3-digit value, but the slider handles it with no problem. + +``` +double getHorizontalShift({ + + RenderBox parentBox, + + Offset center, + + TextPainter labelPainter, + + double textScaleFactor, + + Size sizeWithOverflow, + + double scale, + +}) { + + assert(!sizeWithOverflow.isEmpty); + + const double edgePadding = 8.0; + + final double rectangleWidth = _upperRectangleWidth(labelPainter, scale, textScaleFactor); + + /// Value indicator draws on the overlay, and by using the global Offset, + + /// we are making sure that we use the bounds of the Overlay instead of the Slider. + + final Offset globalCenter = parentBox.localToGlobal(center); + + // The rectangle must be shifted toward the center so that it minimizes the + + // chance of it rendering outside the bounds of the render box. If the shift + + // is negative, then the lobe is shifted from right to left. If the shift is + + // positive, then the lobe is shifted from left to right. + + final double overflowLeft = math.max(0, rectangleWidth / 2 — globalCenter.dx + edgePadding); + + final double overflowRight = math.max(0, rectangleWidth / 2 — (sizeWithOverflow.width — globalCenter.dx — edgePadding)); + + if (rectangleWidth < sizeWithOverflow.width) { + + return overflowLeft — overflowRight; + + } else if (overflowLeft — overflowRight > 0) { + + return overflowLeft — (edgePadding * textScaleFactor); + + } else { + + return -overflowRight + (edgePadding * textScaleFactor); + + } + +} +``` + + +<DashImage figure src="images/0f7ar1ek7C7pUx5cF.webp" /> + + +Note: This example was made with the `textScaleFactor` set to 4 for better demonstration. + +## Activated value indicator takes precedence + +The `RangeSlider` value indicator was also updated so that the currently active indicator takes precedence over the resting indicator. This means that if the indicators overlap, then the active indicator ‘floats’ over the other one. + +<DashImage figure src="images/0dUrIA4k2wFu48rDe.webp" /> + + +Note: This example was made with the `textScaleFactor` set to 4 for better demonstration. + +## Old value indicator updated + +We kept the `PaddleValueIndicator` widget, and updated it to have better scalability and accessibility. The `PaddleValueIndicator` widget is also printed on the `Overlay` like the `RectangularSliderValueIndicatorShape`. In the following example, you can see that both `Slider` and `RangeSlider` are using the old value indicator. + +<iframe src="https://codepen.io/JoseAlba/embed/qBOXGYx" width="800" height="600" frameborder="0" allowfullscreen></iframe> + + +If you want to continue using the old value indicator, then just wrap your `Slider` widget with `SliderTheme`, and set the `valueIndicatorShape` property in `SliderThemeData` to `PaddleSliderValueIndicatorShape`. The process is identical for `RangeSlider`. Wrap your `RangeSlider` widget with `SliderTheme`, and set the `rangeValueIndicatorShape` property in `SliderThemeData` to `PaddleRangeSliderValueIndicatorShape`. + +<DashImage figure src="images/0LNKN4lwqpBxQZ60A.webp" /> + + +## The active track is bigger than the inactive track + +The other component shapes in `Slider` were also updated. The track shape was changed so that the active part of the track is bigger than the inactive part of the track. This means that if your application uses a left-to-right language, then the left part of the track is bigger than the right. If your application uses a right-to-left language, then the right part of the track is bigger than the left. For `RangeSlider`, the active track is the portion of the track between the two thumbs. + +<DashImage figure src="images/0q81pqrMZMWkCmRLF.webp" /> + + +<DashImage figure src="images/0qIoICKw6wo7NcwOW.webp" /> + + +## Tick mark component + +The tick mark size and positioning changed. The tick marks are now part of the track component instead of extending the end of the track. There is also padding on the tick mark, so it appears within the track component. The size of the tick mark now has a radius of 1. + +<DashImage figure src="images/0PLNNQjK0_zOXok1A.webp" /> + + +## Thumb component has a shadow + +When the thumb slider component is activated, there is now a `Shadow` that appears on the `Overlay`. + +<DashImage figure src="images/0j7DxUmBmRK7Ztd5A.webp" /> + + +## Closing remarks + +You can try these changes by switching to Flutter version 1.18.0–9.0. This version is available only on the master branch during the release of this article. You can switch to the master branch by running `flutter channel master` and then `flutter upgrade`. + +Learn more about the development of the `RangeSlider` in [Material RangeSlider in Flutter](https://medium.com/flutter/material-range-slider-in-flutter-a285c6e3447d). + +*About the author: Jose recently graduated from university and now works on [Material](https://material.io/), a design system that helps teams build high-quality digital experiences. Jose’s team maintains the Flutter [material library](https://api.flutter.dev/flutter/material/material-library.html). To learn more, visit his Jose’s pages on [GitHub](https://github.com/JoseAlba), [LinkedIn](https://www.linkedin.com/in/josealba1996/), [YouTube](https://www.youtube.com/channel/UCOdKA_On0oPe1tz02z1QfxA?view_as=subscriber), and [Instagram](https://www.instagram.com/jose.alba/).* \ No newline at end of file diff --git a/sites/www/content/blog/whats-next-for-flutter/images/01m_CqCW7oMEoqPrl.webp b/sites/www/content/blog/whats-next-for-flutter/images/01m_CqCW7oMEoqPrl.webp new file mode 100644 index 0000000000..eb2ad2adb1 Binary files /dev/null and b/sites/www/content/blog/whats-next-for-flutter/images/01m_CqCW7oMEoqPrl.webp differ diff --git a/sites/www/content/blog/whats-next-for-flutter/images/0aS5btwRMIK-AkltC.webp b/sites/www/content/blog/whats-next-for-flutter/images/0aS5btwRMIK-AkltC.webp new file mode 100644 index 0000000000..d92204b322 Binary files /dev/null and b/sites/www/content/blog/whats-next-for-flutter/images/0aS5btwRMIK-AkltC.webp differ diff --git a/sites/www/content/blog/whats-next-for-flutter/images/0uGTJ0p_XZE_yZ9NS.webp b/sites/www/content/blog/whats-next-for-flutter/images/0uGTJ0p_XZE_yZ9NS.webp new file mode 100644 index 0000000000..f8ed57fe7d Binary files /dev/null and b/sites/www/content/blog/whats-next-for-flutter/images/0uGTJ0p_XZE_yZ9NS.webp differ diff --git a/sites/www/content/blog/whats-next-for-flutter/images/1-DXiE7L987-q6zs8hczbOQ.jpeg b/sites/www/content/blog/whats-next-for-flutter/images/1-DXiE7L987-q6zs8hczbOQ.jpeg new file mode 100644 index 0000000000..4b7e3f9029 Binary files /dev/null and b/sites/www/content/blog/whats-next-for-flutter/images/1-DXiE7L987-q6zs8hczbOQ.jpeg differ diff --git a/sites/www/content/blog/whats-next-for-flutter/images/1LfW_XnTVxV1dL1CLT7iAWQ.jpeg b/sites/www/content/blog/whats-next-for-flutter/images/1LfW_XnTVxV1dL1CLT7iAWQ.jpeg new file mode 100644 index 0000000000..5622408c5a Binary files /dev/null and b/sites/www/content/blog/whats-next-for-flutter/images/1LfW_XnTVxV1dL1CLT7iAWQ.jpeg differ diff --git a/sites/www/content/blog/whats-next-for-flutter/images/1MdyKANeEPV0iIQTTwOBGog.webp b/sites/www/content/blog/whats-next-for-flutter/images/1MdyKANeEPV0iIQTTwOBGog.webp new file mode 100644 index 0000000000..fcdee84fd5 Binary files /dev/null and b/sites/www/content/blog/whats-next-for-flutter/images/1MdyKANeEPV0iIQTTwOBGog.webp differ diff --git a/sites/www/content/blog/whats-next-for-flutter/images/1QjyV-g_N50iwOVNSGglxxA.webp b/sites/www/content/blog/whats-next-for-flutter/images/1QjyV-g_N50iwOVNSGglxxA.webp new file mode 100644 index 0000000000..65f810d1f2 Binary files /dev/null and b/sites/www/content/blog/whats-next-for-flutter/images/1QjyV-g_N50iwOVNSGglxxA.webp differ diff --git a/sites/www/content/blog/whats-next-for-flutter/images/1QrEupNb6Ebc5_4c0PW4fQw.jpeg b/sites/www/content/blog/whats-next-for-flutter/images/1QrEupNb6Ebc5_4c0PW4fQw.jpeg new file mode 100644 index 0000000000..46a3b2d637 Binary files /dev/null and b/sites/www/content/blog/whats-next-for-flutter/images/1QrEupNb6Ebc5_4c0PW4fQw.jpeg differ diff --git a/sites/www/content/blog/whats-next-for-flutter/images/1R-Lktrjfd8lLirhC_BBg5Q.webp b/sites/www/content/blog/whats-next-for-flutter/images/1R-Lktrjfd8lLirhC_BBg5Q.webp new file mode 100644 index 0000000000..30f68b2748 Binary files /dev/null and b/sites/www/content/blog/whats-next-for-flutter/images/1R-Lktrjfd8lLirhC_BBg5Q.webp differ diff --git a/sites/www/content/blog/whats-next-for-flutter/images/1ZaldnkdSaQZqWX5LKWB5Ig.webp b/sites/www/content/blog/whats-next-for-flutter/images/1ZaldnkdSaQZqWX5LKWB5Ig.webp new file mode 100644 index 0000000000..8faed7740c Binary files /dev/null and b/sites/www/content/blog/whats-next-for-flutter/images/1ZaldnkdSaQZqWX5LKWB5Ig.webp differ diff --git a/sites/www/content/blog/whats-next-for-flutter/images/1iIgYbZq3WKnUBctIWBap_Q.webp b/sites/www/content/blog/whats-next-for-flutter/images/1iIgYbZq3WKnUBctIWBap_Q.webp new file mode 100644 index 0000000000..c8a07a3952 Binary files /dev/null and b/sites/www/content/blog/whats-next-for-flutter/images/1iIgYbZq3WKnUBctIWBap_Q.webp differ diff --git a/sites/www/content/blog/whats-next-for-flutter/images/1xoVH5v5x7znMLB6NTjcqCA.jpeg b/sites/www/content/blog/whats-next-for-flutter/images/1xoVH5v5x7znMLB6NTjcqCA.jpeg new file mode 100644 index 0000000000..39074eff6b Binary files /dev/null and b/sites/www/content/blog/whats-next-for-flutter/images/1xoVH5v5x7znMLB6NTjcqCA.jpeg differ diff --git a/sites/www/content/blog/whats-next-for-flutter/index.md b/sites/www/content/blog/whats-next-for-flutter/index.md new file mode 100644 index 0000000000..ef949c9e63 --- /dev/null +++ b/sites/www/content/blog/whats-next-for-flutter/index.md @@ -0,0 +1,125 @@ +--- +title: "What’s next for Flutter" +description: "Setting out our vision at the Flutter Forward event in Nairobi" +publishDate: 2023-01-25 +author: timsneath +image: images/01m_CqCW7oMEoqPrl.webp +category: announcements +layout: blog +--- + +Today we’re excited to share our vision for Flutter at [Flutter Forward](https://flutter.dev/events/flutter-forward), our developer event streaming live from Nairobi, Kenya, as developers from around the world gather in-person and online to learn about future directions for Flutter. + +Flutter is a UI toolkit that enables app developers to build for mobile, web, and desktop from a single codebase. Flutter enables you to build **beautiful** apps, giving you complete control over every pixel on the screen. It’s **fast**, taking advantage of your phone or computer’s hardware with support for hardware-accelerated graphics and natively-compiled machine code. It’s **productive**, with technologies like stateful hot reload that allow you to immediately see code changes in your app. Its **portability** enables you to deploy to a wide variety of platforms with the same source code and without surprises. And it’s fully **open source**, with no cost to license it or need to pay for tools to develop for it. + +Flutter has proven popular, with over 700,000 published apps to date: from [small startups with big ideas](https://flutter.dev/showcase/so-vegan) to [enterprises with critical needs](https://flutter.dev/showcase/credit-agricole). At Google, too, we’re finding Flutter a valuable tool, enabling teams like [Google Classroom](https://edu.google.com/workspace-for-education/classroom/) to deliver high-quality solutions to mobile and web users; we’re also adding support for it in our other developer tools including [Google Ads](https://developers.google.com/admob/flutter/quick-start), [Google Maps](https://codelabs.developers.google.com/codelabs/google-maps-in-flutter#0), [Google Pay](https://pub.dev/packages/pay), and of course [Firebase](https://firebase.google.com/docs/flutter/setup?platform=ios). + +<YoutubeEmbed id="JVJF_M9bgj4" title="Google Classroom and Flutter" fullwidth="true"/> + +> “With Flutter, we reduced the code size by 66% for the same functionality… this means less bugs for each platform and less tech debt in the future.” (Kenechi Ufondu, Software Engineer, Google Classroom) + +The first release of Flutter provided a UI toolkit for building mobile apps on Android and iOS, but with Flutter 3, we expanded production support to include Windows, macOS, Linux, and the web, while also enabling embedded platforms to use Flutter. Building on that, [this week we shipped our latest stable release, Flutter 3.7](https://medium.com/flutter/whats-new-in-flutter-3-7-38cbea71133c), which adds a broad set of new features including a new rendering engine on iOS, enhanced support for Material 3 and iOS-style widgets, revamped support for internationalization, background processing improvements, and updates to developer tooling. + +But at this event, we’re looking ***forward***, offering a preview of the next wave of investments we’re making in Flutter: breakthrough graphics performance, seamless integration for web and mobile, early support for new and emerging architectures, and a continued focus on developer experience. We’re showcasing work that will gradually be delivered over the coming months, and we hope that this leaves you excited about our vision for Flutter as a powerful toolkit for any developer who wants to build high-quality, beautiful user experiences that can go anywhere pixels are painted. + +*A disclaimer: the features we’re previewing here are still under development and may change significantly over the coming months. Showing them at this early stage is designed to give early adopters a chance to contribute.* + +<DashImage figure src="images/01m_CqCW7oMEoqPrl.webp" /> + + +## Breakthrough graphics performance + +Historically, cross-platform frameworks have required compromise on visuals, due to the challenges of creating an abstraction layer. Flutter has taken a different approach than most, with its own rendering layer that offers hardware-accelerated graphics and a consistent visual appearance on every device. Moving forward, we will invest in *breakthrough graphics performance* that extends Flutter’s existing strength in this area. + +At Flutter Forward, we’re showing more progress on [**Impeller**](https://github.com/flutter/flutter/wiki/Impeller), Flutter’s next generation rendering engine. Impeller is optimized for Flutter, giving us even more flexibility and control over the graphics pipeline, and opening new opportunities for us. Impeller provides more predictable performance, using precompiled shaders that alleviate dropped frames at runtime caused by shader compilation. It takes advantage of primitives in Metal and Vulkan, the modern low-level APIs in iOS and Android. And it makes effective use of concurrency, distributing single-frame workloads across threads. + +<DashImage figure src="images/1QrEupNb6Ebc5_4c0PW4fQw.jpeg" /> + + +<DashImage figure src="images/1-DXiE7L987-q6zs8hczbOQ.jpeg" /> + + +<DashImage figure src="images/1xoVH5v5x7znMLB6NTjcqCA.jpeg" alt="Impeller provides silky-smooth performance for demanding graphical apps like [Wonderous](https://wonderous.app), a beautiful app that takes you on a tour of the world’s wonders. Shown here: the latest version of Wonderous, which adapts its UI for different devices and form factors, available at [https://wonderous.app.](https://wonderous.app.)" caption="Impeller provides silky-smooth performance for demanding graphical apps like [Wonderous](https://wonderous.app), a beautiful app that takes you on a tour of the world’s wonders. Shown here: the latest version of Wonderous, which adapts its UI for different devices and form factors, available at [https://wonderous.app.](https://wonderous.app.)" /> + + +In addition to offering a silky-smooth UI, Impeller can drastically improve performance in certain scenarios. One demo (shown below) demonstrates a best-case example of this. On the left is a kaleidoscope app using SVG clipping, built using the current default renderer. Scrolling down the page, performance drops with rendering taking longer than the per-frame budget, leading to a frame rate of 7–10 fps. The right side shows the same app running under Impeller, rendering at a glitch-free 60 fps. + +<YoutubeEmbed id="Z7v-YRdHusM" title="Flutter Forward: Impeller side-by-side demo" fullwidth="true"/> + + +One advantage of building from the ground up is that the architecture can support brand new use cases. Newly introduced **support for custom shaders** is already leading to some [amazing new demos](https://twitter.com/reNotANumber/status/1599717360096620544) that show seamless integration with the Flutter widget hierarchy. Our work here isn’t limited to mobile devices, though: we now have early support for custom shaders on the web too, with the same code now powering hardware-accelerated experiences across iOS, Android, and the browser. + +<DashImage figure src="images/0aS5btwRMIK-AkltC.webp" alt="Flutter now supports pixel shaders on the web, enabling various cool visual effects. (Credit: [Erick Ghaumez](https://twitter.com/rxlabz).)" caption="Flutter now supports pixel shaders on the web, enabling various cool visual effects. (Credit: [Erick Ghaumez](https://twitter.com/rxlabz).)" /> + + +And now we’re starting early work on supporting **3D with Flutter**. During the keynote, we showed that you can import models created with [Blender](https://www.blender.org/), and even use hot reload to iterate with Blender in real-time and see the results in a running app. It’s early days, but we’re excited about the initial performance we’re getting, along with the potential of integrating 3D into other Flutter experiences. + +<DashImage figure src="images/1ZaldnkdSaQZqWX5LKWB5Ig.webp" /> + + +<DashImage figure src="images/1MdyKANeEPV0iIQTTwOBGog.webp" /> + + +<DashImage figure src="images/1R-Lktrjfd8lLirhC_BBg5Q.webp" alt="Impeller enables Flutter to render 3D graphics, as shown in this fun demo of Dash." caption="Impeller enables Flutter to render 3D graphics, as shown in this fun demo of Dash." /> + + +Adding 3D and custom shaders raises the bar for graphics performance in a portable UI toolkit to a new level. We’re very excited to see what you do with these new features as they become available. + +## Seamless integration for web and mobile + +While you can write an app entirely using Flutter and Dart, almost any non-trivial project needs to connect with other code. On the web, Flutter may be used as an embedded component within a larger app; on mobile, apps may need to call system APIs or other code written in another language. Our second major theme of investment is therefore *seamless integration for web and mobile.* + +On the web, we’re previewing a new feature that we’re calling **element embedding**, which allows Flutter content to be added to any standard web `<div>`. When embedded in this way, Flutter just becomes a web component, integrating cleanly with the web DOM and even enabling the use of CSS selectors and transformations to style the parent Flutter object. + +We’re also making some big changes to the [js](http://pub.dev/packages/js) package, to enable seamless **interoperability between JavaScript and Dart** code. With js, you can annotate any function in your Dart code with a `@JSExport` attribute, and then call it from your JavaScript code. + +Combining these two new features opens up some exciting new scenarios for using Flutter on the web. In [a proof-of-concept demo](https://flutter-forward-demos.web.app/#/) that we showed at Flutter Forward, you can see a simple Flutter app embedded in a HTML-based webpage. Using CSS, we apply an animated rotation effect; even while rotating, the Flutter content remains interactive. The demo also shows that you can use an HTML button and JavaScript event handler to make changes to the Flutter state, and vice versa. When this work is complete, we think this will open up many new opportunities to use Flutter to add interactivity to an existing web app. + +<DashImage figure src="images/1QjyV-g_N50iwOVNSGglxxA.webp" alt="With Element Embedding, you can embed Flutter into a <div> element and use CSS to style it." caption="With Element Embedding, you can embed Flutter into a <div> element and use CSS to style it." /> + + +Turning to Android and iOS, Flutter has long supported integration with system APIs using platform channels, which offer a message-based approach for communicating with code written in a language like Kotlin or Swift. While this unlocks access to those libraries, it requires an app author to be conversant with multiple languages and requires a fair amount of boilerplate code. + +We’re now starting work on **a new approach for system interoperability** that allows libraries to be called directly. On iOS, we’re building on our work on FFI for C interoperability, adding support for Swift and Objective-C libraries. On Android, we’re using JNI to bridge to Jetpack libraries written in Kotlin. With a new command, Dart automatically creates bindings for cross-language interoperation and converting data classes appropriately. When complete, we aspire that this work will enable a Flutter developer to call a new Jetpack or iOS library without needing to use a plugin or learn a different API syntax, as well as greatly easing the work for a plugin author. [Check out our sample](https://github.com/flutter/samples/tree/main/experimental/pedometer) to learn more! + +## Early to new and emerging architectures + +Thanks to Dart’s broad support for a wide range of processor architectures, as well as its highly-optimized JavaScript compiler, Flutter can already run on a broad variety of devices and form factors. But with some exciting new options on the horizon, our third theme is bringing *early support to new and emerging architectures*. + +[WebAssembly](https://webassembly.org/) has been growing in maturity as a platform-neutral binary instruction format, with [growing support on modern browsers](https://caniuse.com/wasm). Excitingly, WebAssembly opens the web platform to other languages beyond JavaScript. Over recent months, we’ve been collaborating with the Chrome team and other WebAssembly partners to add [early support for garbage-collected languages](https://github.com/WebAssembly/gc) like Dart. This new extension to WebAssembly is now supported with a flag in the latest development builds of Chrome. At Flutter Forward, we’re unveiling early support for **Flutter compilation to WebAssembly**, which will enable further speed and size optimizations for our web support. + +Another platform architecture that is generating growing interest is [RISC-V](https://en.wikipedia.org/wiki/RISC-V), an open standard instruction set architecture (ISA) designed for broad usage. The Android team recently gave a talk on [their work to support RISC-V](https://www.youtube.com/watch?v=70O_RmTWP58), and we’re happy to also announce that **Dart now supports RISC-V**, as part of our own journey to enable Flutter to run on RISC-V devices as they become available. Production RISC-V hardware is still in its infancy, but at Flutter Forward we demonstrated our progress to date on the [ClockworkPi DevTerm Kit R-01](https://www.clockworkpi.com/product-page/devterm-kit-r01), a self-assembly portable terminal running Linux. We believe RISC-V support will be of particular interest for embedded scenarios, where Flutter can provide a powerful UI toolkit for a diverse set of needs. + +<DashImage figure src="images/1LfW_XnTVxV1dL1CLT7iAWQ.jpeg" alt="The ClockworkPi DevTerm R-01, an experimental RISC-V computer, running a Dart console app." caption="The ClockworkPi DevTerm R-01, an experimental RISC-V computer, running a Dart console app." /> + + +## Continued focus on developer experience + +Foundational to everything we’ve described above is developer productivity, which has been a hallmark of Flutter since its inception, with features like stateful hot reload. Our fourth and final area of investment for the future is a *continued focus on developer experience*, across both Flutter and Dart. + +At Flutter Forward, we’re previewing some of our early work on some major new Dart language features that we hope demonstrate our commitment to tasteful evolution of the language. In our development channel, we now have early support for **records and patterns**, two new enhancements that work particularly well together. + +<DashImage figure src="images/0uGTJ0p_XZE_yZ9NS.webp" alt="A simple example of records and patterns being used to return and receive multiple function parameters." caption="A simple example of records and patterns being used to return and receive multiple function parameters." /> + + +We’re also **formally announcing Dart 3**, which is the culmination of our work to introduce sound null safety to the language. Dart 3 also removes other long-deprecated features to further modernize the language. We have started publishing alpha-quality builds of Dart 3, along with matching Flutter builds, enabling developers to test packages and apps. For more information on Dart 3, check out the [separate blog post on the Dart channel](https://medium.com/dart-lang/dart-3-alpha-f1458fb9d232). + +We’re investing in the developer experience for Flutter as well, of course. Following on from the success of the casual game toolkit we [announced at I/O](https://medium.com/flutter/announcing-the-flutter-casual-games-toolkit-c22e401d8fee) last year, we’re sharing [the first version of our **news toolkit**](https://medium.com/flutter/announcing-the-flutter-news-toolkit-180a0d32c012), which accelerates mobile development for news publishers and other content providers who want to reach mobile users without having to design an app from scratch. It includes everything you need to build an article-centric app, with navigation and search, authentication, ad integrations, notifications, profiles and subscriptions, while incorporating best practices from Google News Initiative’s research. We’re today featuring three early adopters here in Africa who are building apps based on this toolkit, including [Hespress](https://www.hespress.com/), one of the largest news sites in Morocco, [Bold Sports](https://boldsportsng.com/), a popular the Nigerian sports site; and [The Standard](https://www.standardmedia.co.ke/), the oldest newspaper in Kenya. + +<YoutubeEmbed id="YnJwjZ3jCTc" title="FOO041 v8" fullwidth="true"/> + + +## Moving forward together + +We hope you share our excitement about where we’re taking Flutter moving forward. We’re continuing to invest heavily in the core developer experience, while making some foundational improvements that pave the way for ourselves and others to build even more powerful experiences with Flutter. + +This image summarizes our direction: + +<DashImage figure src="images/1iIgYbZq3WKnUBctIWBap_Q.webp" /> + + +Beyond our own development themes, it’s gratifying to see how the Flutter ecosystem continues to expand, like [FlutterFlow](https://flutterflow.io/) is a low-code builder for developing native mobile applications, and [Widgetbook](http://widgetbook.io), which provides flexible tooling for designers and developers to collaborate on user interface development. + +In closing, it has been wonderful to come to Kenya and see the entrepreneurship and talent of local developers. The vibrancy of the Flutter community here in Africa is special; the Flutter Kenya user group alone has over 1,000 developers. And we’re thrilled that Flutter can provide new opportunities for developers across Africa to participate in the fast-growing app economy. Klasha is a great example of this promise, using Flutter to quickly get to market and solve local problems. Here’s a video of their experience with Flutter: + +<YoutubeEmbed id="o8NPZ2ofLbI" title="FL018 v4 Grade v2 YT" fullwidth="true"/> diff --git a/sites/www/content/blog/when-should-i-useanimatedbuilder-or-animatedwidget/images/1BdRzZMnfFtwe0Wn1dxWp4g.gif b/sites/www/content/blog/when-should-i-useanimatedbuilder-or-animatedwidget/images/1BdRzZMnfFtwe0Wn1dxWp4g.gif new file mode 100644 index 0000000000..469c7f988f Binary files /dev/null and b/sites/www/content/blog/when-should-i-useanimatedbuilder-or-animatedwidget/images/1BdRzZMnfFtwe0Wn1dxWp4g.gif differ diff --git a/sites/www/content/blog/when-should-i-useanimatedbuilder-or-animatedwidget/images/1nXKMdHu2YNqZmrGClWlDZw.webp b/sites/www/content/blog/when-should-i-useanimatedbuilder-or-animatedwidget/images/1nXKMdHu2YNqZmrGClWlDZw.webp new file mode 100644 index 0000000000..2555f65f62 Binary files /dev/null and b/sites/www/content/blog/when-should-i-useanimatedbuilder-or-animatedwidget/images/1nXKMdHu2YNqZmrGClWlDZw.webp differ diff --git a/sites/www/content/blog/when-should-i-useanimatedbuilder-or-animatedwidget/index.md b/sites/www/content/blog/when-should-i-useanimatedbuilder-or-animatedwidget/index.md new file mode 100644 index 0000000000..108bdbb9ec --- /dev/null +++ b/sites/www/content/blog/when-should-i-useanimatedbuilder-or-animatedwidget/index.md @@ -0,0 +1,292 @@ +--- +title: "When should I useAnimatedBuilder or AnimatedWidget?" +description: "We know you have many choices when you fly, I mean animate, in Flutter, so thank you for choosing AnimatedBuilder and AnimatedWidget. Wait…" +publishDate: 2020-01-19 +author: efortuna +image: images/1BdRzZMnfFtwe0Wn1dxWp4g.gif +category: tutorial +layout: blog +--- + +We know you have many choices when you fly, I mean animate, in Flutter, so thank you for choosing `AnimatedBuilder` and `AnimatedWidget`. Wait, what? No! Flutter has many different animation widgets, but unlike commercial airlines, each type of widget is right for a different type of job. Sure, you can accomplish the same animation in a couple of different ways, but using the right animation widget for the job will make your life much easier. + +This article covers why you might want to use `AnimatedBuilder` or `AnimatedWidget` versus other animation widgets, and how to use them. Suppose you want to add an animation to your app. This article is [part](https://medium.com/flutter/flutter-animation-basics-with-implicit-animations-95db481c5916) [of](https://medium.com/flutter/custom-implicit-animations-in-flutter-with-tweenanimationbuilder-c76540b47185) [a](https://medium.com/flutter/directional-animations-with-built-in-explicit-animations-3e7c5e6fbbd7) series, walking through why you might want to use each type of animation widget. The particular animation you want to do repeats a couple of times or needs to be able to pause and resume in response to something, like a finger tap. Because your animation needs to repeat, or stop and start, you’ll need to use an *explicit* animation. + +As a reminder, we have two broad categories of animations in Flutter: explicit and implicit. For explicit animations, you need an animation controller. For implicit animations, you don’t. We introduced animation controllers in the previous article about built-in explicit animations. If you’d like to learn more about those, please check that out first. + +So, if you’ve determined that you need an explicit animation, there are a whole host of explicit animation classes for you to choose from. Those are the classes generally named `FooTransition`, where *Foo* is the name of the property you are trying to animate. I recommend seeing if you can use one of those widgets to accomplish your needs first, *before* diving into the deep world of `AnimatedWidget` and `AnimatedBuilder`. There’s an amazing selection of widgets for pretty much anything you can think of — rotation, position, alignment, fading, text style, and many more. Plus, you can compose these widgets, so that you can rotate *and* fade. But, if none of those built-in widgets can do what you’re looking for, it’s time to build your own using `AnimatedWidget` or `AnimatedBuilder`. + +<DashImage figure src="images/1nXKMdHu2YNqZmrGClWlDZw.webp" alt="A flow chart for understanding what animation to use in any scenario. This article focuses on the two bottom blue end-states, AnimatedBuilder and AnimatedWidget." caption="A flow chart for understanding what animation to use in any scenario. This article focuses on the two bottom blue end-states, AnimatedBuilder and AnimatedWidget." /> + + +## Specific Example + +To make this more concrete, let’s walk through a specific scenario: I want to write an app with an alien spaceship and have a spaceship beam animation. + +<DashImage figure src="images/1BdRzZMnfFtwe0Wn1dxWp4g.gif" alt="Maybe something exactly like this." caption="Maybe something exactly like this." /> + + +I drew a spaceship beam with a gradient that fades from yellow to transparent, beginning the fade at the very center of the gradient. Then, I created the “beam” shape from that gradient with a path clipper. + +```dart +import 'package:flutter/material.dart'; + +void main() => runApp(MyApp()); + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + body: MyHomePage(), + )); + } +} + +class MyHomePage extends StatelessWidget { + final Image starsBackground = Image.asset( + 'assets/milky-way.jpg', + ); + final Image ufo = Image.asset('assets/ufo.png'); + @override + Widget build(BuildContext context) { + return Stack( + alignment: AlignmentDirectional.center, + children: <Widget>[ + starsBackground, + ClipPath( + clipper: const BeamClipper(), + child: Container( + height: 1000, + decoration: BoxDecoration( + gradient: RadialGradient( + radius: 1.5, + colors: [ + Colors.yellow, + Colors.transparent, + ], + ), + ), + ), + ), + ufo, + ], + ); + } +} + +class BeamClipper extends CustomClipper<Path> { + const BeamClipper(); + + @override + getClip(Size size) { + return Path() + ..lineTo(size.width / 2, size.height / 2) + ..lineTo(size.width, size.height) + ..lineTo(0, size.height) + ..lineTo(size.width / 2, size.height / 2) + ..close(); + } + + /// Return false always because we always clip the same area. + @override + bool shouldReclip(CustomClipper oldClipper) => false; +} + +``` + +I want to create a “beam down” animation, starting from the center of that gradient, and I want to make it repeat. That means I’ll need to create an explicit animation. Unfortunately there’s no built-in explicit animation to animate funnel-shaped gradients, but you know what we do have… `AnimatedWidget` and `AnimatedBuilder` to do the trick! + +## AnimatedBuilder + +To make the beam animate, I am going to wrap this gradient code in an `AnimatedBuilder` widget. The gradient code goes in the builder function, which gets called when the `AnimatedBuilder` builds. + +Next I need to add a controller to drive this animation. The controller provides the values that `AnimatedBuilder` uses to draw new versions of the animation frame by frame. As you saw in the previous article, I mix in the `SingleTickerProviderStateMixin` class and instantiate the controller in the `initState` function so that it only gets created once. I create the controller in `initState`, rather than the `build` method, because I don’t want to create this controller multiple times — I want it to provide new values to animate with for each frame! Because I created a new object in `initState`, I add a dispose method and tell Flutter that it can get rid of that controller when the parent widget is no longer on the screen. + +Then, I pass that controller to the `AnimatedBuilder`, and my animation runs as expected! + +```dart +class MyHomePage extends StatefulWidget { + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State<MyHomePage> + with SingleTickerProviderStateMixin { + final Image starsBackground = Image.asset( + 'assets/milky-way.jpg', + ); + final Image ufo = Image.asset('assets/ufo.png'); + AnimationController _animation; + + @override + void initState() { + super.initState(); + _animation = AnimationController( + duration: const Duration(seconds: 5), + vsync: this, + )..repeat(); + } + + @override + Widget build(BuildContext context) { + return Stack( + alignment: AlignmentDirectional.center, + children: <Widget>[ + starsBackground, + AnimatedBuilder( + animation: _animation, + builder: (_, __) { + return ClipPath( + clipper: const BeamClipper(), + child: Container( + height: 1000, + decoration: BoxDecoration( + gradient: RadialGradient( + radius: 1.5, + colors: [ + Colors.yellow, + Colors.transparent, + ], + stops: [0, _animation.value], + ), + ), + ), + ); + }, + ), + ufo, + ], + ); + } + + @override + void dispose() { + _animation.dispose(); + super.dispose(); + } +} + +class BeamClipper extends CustomClipper<Path> { + const BeamClipper(); + + @override + getClip(Size size) { + return Path() + ..lineTo(size.width / 2, size.height / 2) + ..lineTo(size.width, size.height) + ..lineTo(0, size.height) + ..lineTo(size.width / 2, size.height / 2) + ..close(); + } + + /// Return false always because we always clip the same area. + @override + bool shouldReclip(CustomClipper oldClipper) => false; +} +``` + +You may recall in the [`TweenAnimationBuilder` article](https://medium.com/flutter/custom-implicit-animations-in-flutter-with-tweenanimationbuilder-c76540b47185) that we used the child parameter as a performance optimization, and we can do that with `AnimatedBuilder` too. Basically, if we have an object that never changes over the course of the animation, we can build it ahead of time, and just pass it to `AnimatedBuilder`. + +In this specific case, a *better* way to accomplish the same thing is to give `BeamClipper` a `const` constructor and just make it `const`. It’s less code, and the object is created at compile time, making things even faster. Sometimes, though, you’ll be coding something that doesn’t have a const constructor, and that’s a good case for when to make use of that optional child parameter. + +## AnimatedWidget + +So, we have our animation, but the build method that contains the `AnimatedBuilder` code is a little large. If your build method is starting to get hard to read, it’s time to refactor your code! + +You could pull your `AnimatedBuilder` code out into a separate widget, but then you just have a build method inside a build method, which is a little silly. Instead, you can accomplish the same animation by creating a new widget that extends `AnimatedWidget`. I’ll name my widget `BeamTransition` to be consistent with the *FooTransition* naming convention for explicit animations. I pass in the animation controller to `BeamTransition` and reuse the body of the `AnimatedBuilder’s` builder function. + +```dart +class MyHomePage extends StatefulWidget { + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State<MyHomePage> + with SingleTickerProviderStateMixin { + final Image starsBackground = Image.asset( + 'assets/milky-way.jpg', + ); + final Image ufo = Image.asset('assets/ufo.png'); + AnimationController _animation; + + @override + void initState() { + super.initState(); + _animation = AnimationController( + duration: const Duration(seconds: 5), + vsync: this, + )..repeat(); + } + + @override + Widget build(BuildContext context) { + return Stack( + alignment: AlignmentDirectional.center, + children: <Widget>[ + starsBackground, + BeamTransition(animation: _animation), + ufo, + ], + ); + } + + @override + void dispose() { + _animation.dispose(); + super.dispose(); + } +} + +class BeamTransition extends AnimatedWidget { + BeamTransition({Key key, Animation<double> animation}) + : super(key: key, listenable: animation); + @override + Widget build(BuildContext context) { + final Animation<double> animation = listenable; + return ClipPath( + clipper: const BeamClipper(), + child: Container( + height: 1000, + decoration: BoxDecoration( + gradient: RadialGradient( + radius: 1.5, + colors: [ + Colors.yellow, + Colors.transparent, + ], + stops: [0, animation.value], + ), + ), + ), + ); + } +} +``` + +Just like `AnimatedBuilder`, if appropriate, I can add a child parameter to my widget as a performance optimization so that it builds ahead of time, instead of every time I animate. Just a reminder though, in this example, making my `BeamClipper` take a `const` constructor is the best way to go. + +## So really, which one should I to use? + +We just saw how `AnimatedBuilder` and `AnimatedWidget` can both be used to accomplish the same type of explicit animations when you can’t find a built-in explicit animation to do what you want. So, which one should you use? It’s really a matter of taste. Generally speaking, I recommend making individual widgets, each with a separate job — which in this case is animation. + +That’s a vote in favor of using `AnimatedWidget` whenever possible. However, if your parent widget that creates the animation controller is pretty simple, maybe making a separate standalone widget just for your animation is too much extra code. In that case, `AnimatedBuilder` is all that you need. + +This is an article version of the video below. If you prefer videos, click below: + +<YoutubeEmbed id="fneC7t4R_B0" title="Creating custom explicit animations with AnimatedBuilder & AnimatedWidget - Flutter in Focus" fullwidth="true"/> + + +Articles in this series: + +* [How to Choose Which Flutter Animation Widget is Right for You?](https://medium.com/p/79ecfb7e72b5/edit) + +* [Flutter animation basics with implicit animations](https://medium.com/flutter/flutter-animation-basics-with-implicit-animations-95db481c5916) + +* [Custom Implicit Animations in Flutter…with TweenAnimationBuilder](https://medium.com/flutter/custom-implicit-animations-in-flutter-with-tweenanimationbuilder-c76540b47185) + +* [Directional animations with built-in explicit animations](https://medium.com/flutter/directional-animations-with-built-in-explicit-animations-3e7c5e6fbbd7) + +* [When should I useAnimatedBuilder or AnimatedWidget?](https://medium.com/flutter/when-should-i-useanimatedbuilder-or-animatedwidget-57ecae0959e8) (this article) + +* [Animation deep dive](https://medium.com/flutter/animation-deep-dive-39d3ffea111f) \ No newline at end of file diff --git a/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/images/00d3CyUdb9K75qjt2.webp b/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/images/00d3CyUdb9K75qjt2.webp new file mode 100644 index 0000000000..a8790dcb32 Binary files /dev/null and b/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/images/00d3CyUdb9K75qjt2.webp differ diff --git a/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/images/09CO8AmwyqIudhgK6.webp b/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/images/09CO8AmwyqIudhgK6.webp new file mode 100644 index 0000000000..23a256c6cb Binary files /dev/null and b/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/images/09CO8AmwyqIudhgK6.webp differ diff --git a/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/images/0VLupMT920gd2acrw.webp b/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/images/0VLupMT920gd2acrw.webp new file mode 100644 index 0000000000..b021f5b9f8 Binary files /dev/null and b/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/images/0VLupMT920gd2acrw.webp differ diff --git a/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/images/0_m-0usdhsHPmeZ8U.webp b/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/images/0_m-0usdhsHPmeZ8U.webp new file mode 100644 index 0000000000..04cadd6132 Binary files /dev/null and b/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/images/0_m-0usdhsHPmeZ8U.webp differ diff --git a/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/images/0d0IHqqnl0KoeLuT8.webp b/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/images/0d0IHqqnl0KoeLuT8.webp new file mode 100644 index 0000000000..c1318019d6 Binary files /dev/null and b/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/images/0d0IHqqnl0KoeLuT8.webp differ diff --git a/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/images/0lmC1UJVINDIXumGt.webp b/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/images/0lmC1UJVINDIXumGt.webp new file mode 100644 index 0000000000..eaa9f80396 Binary files /dev/null and b/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/images/0lmC1UJVINDIXumGt.webp differ diff --git a/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/index.md b/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/index.md new file mode 100644 index 0000000000..e48d34f527 --- /dev/null +++ b/sites/www/content/blog/which-factors-affected-users-decisions-to-adopt-flutter-q1-2021-user-survey-results/index.md @@ -0,0 +1,83 @@ +--- +title: "Which factors affected users’ decisions to adopt Flutter? — Q1 2021 user survey results" +description: "The Flutter team is here to share the results from this quarter’s user survey! This quarter, we collected over 8,000 responses over 7 days, between March 5th and 11th. The goal of this quarterly survey program is to hear from you, in a structured format, so that the Flutter team can focus on things that matter most to our users. Results from the previous surveys are published on Medium too." +publishDate: 2021-05-13 +author: jayoung-lee +image: images/0_m-0usdhsHPmeZ8U.webp +category: announcements +layout: blog +--- + +## Which factors affected users’ decisions to adopt Flutter? — Q1 2021 user survey results + +## User satisfaction + +In this quarter’s survey, 92% of the 8,652 respondents said that they are positively satisfied with Flutter (55% very satisfied, 37% somewhat satisfied), similar to the last quarter’s results. + +What’s notable this quarter is that 89% of the respondents were also positively satisfied with the Dart language, which is an all-time high score. This was very encouraging news to the team, because the percentage started at 80 in 2019. The Dart team did tremendous work to improve the developers’ experience with the Dart language to achieve this satisfaction rate, including [sound null safety](https://dart.dev/null-safety). We’re very happy to share this survey result, and we thank those who acknowledged the unique strengths of the language. + +<DashImage figure src="images/09CO8AmwyqIudhgK6.webp" alt="*The percentage of users who are positively satisfied with Dart increased 9%, from 80% in Q3 2019 to 89% in Q1 2021.*" caption="*The percentage of users who are positively satisfied with Dart increased 9%, from 80% in Q3 2019 to 89% in Q1 2021.*" /> + + +## Which factors affected users’ decisions to adopt Flutter? + +The Flutter team wanted to understand how different factors affect users’ decisions to adopt Flutter for their new projects. The goal was to find areas that the team should invest in to alleviate users’ concerns and delight our users. In addition, by sharing the results, we hope that you can also use the information to find ways to contribute to the Flutter community or to advocate for Flutter in your organization or community. + +Because this survey targeted existing Flutter users, we asked about the users’ recent experience with evaluating Flutter for their next new project. We learned that 75% of the respondents evaluated Flutter against other technologies in the last 3 months and that 67% of them were for business reasons, followed by learning (17%) and hobby (14%). + +First, we asked what made users lean toward adopting Flutter. As shown in the following chart, users leaned toward adopting Flutter mainly because Flutter supports the following features, listed by importance: + +1. A single codebase. + +1. Ready-made UI widgets. + +1. Hot reload. + +<DashImage figure src="images/0lmC1UJVINDIXumGt.webp" alt="*Factors that made developers lean toward adopting Flutter for their next project*" caption="*Factors that made developers lean toward adopting Flutter for their next project*" /> + + +Next, we asked what made users lean against adopting Flutter. The users were most concerned about the following, listed in order of importance: + +1. Google’s commitment to Flutter. + +1. Maturity of Flutter for the scale of the app they’re building. + +1. Packages and plugins available in Flutter’s ecosystem. + +<DashImage figure src="images/00d3CyUdb9K75qjt2.webp" alt="*Factors that made developers lean against adopting Flutter for their next project*" caption="*Factors that made developers lean against adopting Flutter for their next project*" /> + + +From these results, the team learned that users fear whether Flutter will be around for the long term. It’s a reasonable concern, because you’re tying your career or apps to our framework. So, we want to mention the scale of internal adoption again. At Flutter Engage, we shared that there are more than 30 teams at Google who are building mission-critical apps with Flutter. These are teams like [Google Pay, Google Ads, Nest Hub, and Stadia](https://youtu.be/IdrCyS7EF8M?t=67) who have thousands of developers committed to Flutter development. Flutter will be prominently featured at [Google I/O](https://events.google.com/io/) again this year. Stay tuned for our latest announcements, and sign up for the Flutter sessions listed on the[ I/O website](https://events.google.com/io/program/content?4=topic_flutter&lng=en), starting May 5th. + +We also want to point out that Flutter isn’t only growing because of support from Google — there are more contributors outside of Google than inside. A vibrant, open source community of developers around the world is contributing to our shared success! Take a look at the other large and small contributors like [Toyota](https://youtu.be/IdrCyS7EF8M?t=237) or Ubuntu. + +The next concern was about the maturity of Flutter, including the ecosystem around it. We’re investing in developing the packages and plugin ecosystem and working on the stability and quality of the core framework. We see the sentiment improving here with the release of Flutter 2 and an ever-growing ecosystem supporting it. Now that the web support is stable and the desktop support is in beta, our main themes in upcoming releases will be around polishing the framework and improving the productivity that you can achieve with Flutter. In the meantime, you can check out the [showcase section](https://flutter.dev/showcase) of the Flutter website and see how major brands like BMW, eBay, Nubank, Tencent, Square, and Sonos are building with Flutter. + +We also collected thousands of raw comments about various topics in response to open-ended questions. We had at least three different workshops where engineers in the Flutter subteams gathered to read and digest comments about various topics, including ecosystem, developer tools, and so on. The ideas generated during the workshop will be used as input for the team’s goals for the coming quarters. + +<DashImage figure src="images/0_m-0usdhsHPmeZ8U.webp" alt="*Online post-it notes that the Flutter team used to generate and organize ideas about developer tools, based on the comments from the survey*" caption="*Online post-it notes that the Flutter team used to generate and organize ideas about developer tools, based on the comments from the survey*" /> + + +There are things that community contributors can do too. We learned from the user comments that many Flutter developers still want to have higher-quality packages with better maintenance, even though they acknowledge that Flutter’s ecosystem has matured over time. There were a lot of mentions of multimedia packages (like audio and video players) and Bluetooth packages that need better support. We welcome you to contribute to those areas and expand the coverage, by, for example, submitting PRs to existing packages, as the Flutter team strives to provide high-quality, [first-party packages](https://pub.dev/publishers/flutter.dev/packages). + +## Cloud-based services and products + +The Flutter team knows that there is an interest for cloud services among some Flutter users, but the team didn’t have a good understanding of what types of cloud solutions users are looking for. Thanks to the survey, we learned that 40% of the developers use cloud-based services, and 27% plan to use one for their Flutter projects. + +For those who are already using cloud-based services, the most popular cloud solution was Firebase (82%), followed by Google Cloud Platform (GCP) (34%), and Amazon Web Services (AWS) (25%). + +<DashImage figure src="images/0VLupMT920gd2acrw.webp" /> + + +Users said that they chose Firebase and GCP, because they were easy to use with Flutter. Many users who chose AWS and Azure said that it was because of existing familiarity. + +<DashImage figure src="images/0d0IHqqnl0KoeLuT8.webp" /> + + +The Flutter team is working closely with Firebase and GCP. We will continue to support [FlutterFire plugins](https://firebase.flutter.dev/), and the team is also working with the GCP team to make it easier to use with Flutter. + +## What’s next? + +The Flutter UXR team will be running surveys every quarter. You’ll be able to find the announcement on [flutter.dev](https://flutter.dev/), in the Flutter IDE plugin, or on Twitter @FlutterDev. You can also participate in other research by signing up for upcoming UX studies. Please continue contributing your thoughts as the team seeks answers to important questions. + +Again, thank you to everyone who participated in this survey and provided valuable feedback. We read every piece of feedback and take it to heart. Our goal is to build a product that you love, and we appreciate your time and effort helping us! \ No newline at end of file diff --git a/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1CWtefgiPMBwNi8Q1dxPI5A.webp b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1CWtefgiPMBwNi8Q1dxPI5A.webp new file mode 100644 index 0000000000..73d953fffa Binary files /dev/null and b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1CWtefgiPMBwNi8Q1dxPI5A.webp differ diff --git a/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1G2Bl4jOqWjj1s3ojNLgERQ.webp b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1G2Bl4jOqWjj1s3ojNLgERQ.webp new file mode 100644 index 0000000000..2bb3495fe1 Binary files /dev/null and b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1G2Bl4jOqWjj1s3ojNLgERQ.webp differ diff --git a/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1N2U8dMlt2pD5ozUGHOTXwg.webp b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1N2U8dMlt2pD5ozUGHOTXwg.webp new file mode 100644 index 0000000000..2ca12c100e Binary files /dev/null and b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1N2U8dMlt2pD5ozUGHOTXwg.webp differ diff --git a/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1RLKlDP0xpOdbQYkdzNtVFw.jpeg b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1RLKlDP0xpOdbQYkdzNtVFw.jpeg new file mode 100644 index 0000000000..b1e8acd511 Binary files /dev/null and b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1RLKlDP0xpOdbQYkdzNtVFw.jpeg differ diff --git a/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1Tt9lpvpKa1700v74KcjcIw.webp b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1Tt9lpvpKa1700v74KcjcIw.webp new file mode 100644 index 0000000000..20e785853f Binary files /dev/null and b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1Tt9lpvpKa1700v74KcjcIw.webp differ diff --git a/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1WiPwyMCRrVn_iit7HiG4Pg.webp b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1WiPwyMCRrVn_iit7HiG4Pg.webp new file mode 100644 index 0000000000..81d2307951 Binary files /dev/null and b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1WiPwyMCRrVn_iit7HiG4Pg.webp differ diff --git a/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1cxcLBy8_MnUprKE_JliA7w.webp b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1cxcLBy8_MnUprKE_JliA7w.webp new file mode 100644 index 0000000000..dede5089ee Binary files /dev/null and b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1cxcLBy8_MnUprKE_JliA7w.webp differ diff --git a/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1eKLcIR16WZCNmwFcBzL8Zw.webp b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1eKLcIR16WZCNmwFcBzL8Zw.webp new file mode 100644 index 0000000000..08a51e6e3e Binary files /dev/null and b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1eKLcIR16WZCNmwFcBzL8Zw.webp differ diff --git a/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1jSlI0OpsEEqomXhgEbZNTA.webp b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1jSlI0OpsEEqomXhgEbZNTA.webp new file mode 100644 index 0000000000..f4ae7b61c4 Binary files /dev/null and b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1jSlI0OpsEEqomXhgEbZNTA.webp differ diff --git a/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1mWZiUb3dO9aMN1iV8YFxCw.webp b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1mWZiUb3dO9aMN1iV8YFxCw.webp new file mode 100644 index 0000000000..b3d193c22f Binary files /dev/null and b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1mWZiUb3dO9aMN1iV8YFxCw.webp differ diff --git a/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1nQ9gwT4kdN8SIXzxq0XyYg.webp b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1nQ9gwT4kdN8SIXzxq0XyYg.webp new file mode 100644 index 0000000000..16e7ef56ea Binary files /dev/null and b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1nQ9gwT4kdN8SIXzxq0XyYg.webp differ diff --git a/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1t3UOazDW4es-wlm5WhHlLw.webp b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1t3UOazDW4es-wlm5WhHlLw.webp new file mode 100644 index 0000000000..84ae1a3627 Binary files /dev/null and b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1t3UOazDW4es-wlm5WhHlLw.webp differ diff --git a/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1xIdaloHvQPE78oAXLJ7Rzg.webp b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1xIdaloHvQPE78oAXLJ7Rzg.webp new file mode 100644 index 0000000000..79bc1da392 Binary files /dev/null and b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/images/1xIdaloHvQPE78oAXLJ7Rzg.webp differ diff --git a/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/index.md b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/index.md new file mode 100644 index 0000000000..ba2c931637 --- /dev/null +++ b/sites/www/content/blog/why-flutter-doesnt-use-oem-widgets/index.md @@ -0,0 +1,231 @@ +--- +title: "Why Flutter doesn’t use OEM widgets" +description: "A developer’s journey from skepticism to excitement for the future of non-OEM widgets, and bespoke mobile design" +publishDate: 2017-11-16 +author: xster +image: images/1Tt9lpvpKa1700v74KcjcIw.webp +category: announcements +layout: blog +--- + +<DashImage figure src="images/1Tt9lpvpKa1700v74KcjcIw.webp" alt="Modern, consistent Material Design on a phone shipped **4 years** before Material Design was unveiled" caption="Modern, consistent Material Design on a phone shipped **4 years** before Material Design was unveiled" /> + + +## Preface + +Before joining the Flutter team early in 2017, I developed mobile applications for years — some in a fast-paced startup agency, and more recently for large-scale consumer applications inside Google. + +Developers from both worlds are acutely aware of the benefits of a well-adopted mobile cross-platform solution. The scalability and productivity gains from such a solution are clear to both big and small players in the mobile app industry. Yet I also admittedly placed myself frequently on the side of caution and skepticism when facing existing development solutions that didn’t use OEM SDKs’ widgets. + +* “Clients don’t want to pay all this money and end up with an app that doesn’t quite feel native”. + +* “Won’t you have to keep playing catch-up vs Google and Apple who have hundreds of smart people building new UI?” + +* “We want to build fast but we can’t compromise on UI quality or performance” + +* “Designing uniform UI for both simultaneously will end up with the lowest common denominator” + +These common questions were very much the same ones I asked myself as well when I first encountered Flutter. + +However, rather than starting from the end results, this is not a point-by-point technical face-off of every Flutter characteristic vs every other application development platform. Instead I’m hoping to use the rest of the article to share what I’ve learned about Flutter’s different approach that excites me about the future of mobile app development. And I want to highlight how Flutter can improve the experience of both mobile app engineers and end-consumers in the mobile space. + +## What do the People want? + +Before diving head-first into how Flutter is built and why OEM widgets weren’t a part of it, it’s worth taking a step back and spending a brief moment reflecting on the end goal of all this. + +This is the part that excites me the most. That is, going back to first principles and thinking about what helps mobile developers most efficiently add value for their users, and what types of products do people want to create. + +To get to the heart of products that resonate the most with the needs of their customers, let’s look through some popular and editorial reviews and awards for past years’ trending apps. For example, MWC’s [Glomo Awards](https://www.globalmobileawards.com/nominee2017/), TIME’s [Best Apps of the Year](http://time.com/4549647/best-apps-year-2016/) and the Webby Awards’s [Mobile Apps](https://www.webbyawards.com/winners/2017/mobile-sites-apps/features-categories/best-practices/) section, with their People’s Voice awards selected by popular votes. + +Here’s a selection of highly acclaimed, award-winning apps we’ve all likely used and can extract learnings from: + +<DashImage figure src="images/1xIdaloHvQPE78oAXLJ7Rzg.webp" /> + + +<DashImage figure src="images/1cxcLBy8_MnUprKE_JliA7w.webp" /> + + +<DashImage figure src="images/1nQ9gwT4kdN8SIXzxq0XyYg.webp" /> + + +<DashImage figure src="images/1CWtefgiPMBwNi8Q1dxPI5A.webp" /> + + +<DashImage figure src="images/1WiPwyMCRrVn_iit7HiG4Pg.webp" /> + + +<DashImage figure src="images/1N2U8dMlt2pD5ozUGHOTXwg.webp" /> + + +<DashImage figure src="images/1jSlI0OpsEEqomXhgEbZNTA.webp" /> + + +<DashImage figure src="images/1eKLcIR16WZCNmwFcBzL8Zw.webp" alt="A collection of 2016’s design award winning apps in both Android and iOS forms" caption="A collection of 2016’s design award winning apps in both Android and iOS forms" /> + + +You’re likely to have already noticed some common patterns here. + +Starting off with an easy one, the first derivation we can make from these examples is that successful products need to appear on both mobile platforms :) + +Looking further now at the screenshots above, one must notice the marked similarity of the products between the two platforms as well as the creative differences between the different products themselves. + +Therefore, I believe the other key observation to make here with respect what the end-users want is that: the most successful developers take their users to the limits of their imaginations, rather than to the molds of stock cookie-cutters. And they do so to offer a unique and tailored user experience best fitted for the specific problem domain their product is trying to solve. + +In other words, in today’s market, a branded, custom UI is king. + +It should also not be a surprise. Web designers have already known this for years, distinguishing their products from competitors with unique, branded (and non-OEM styled) designs. + +In my experience working in a mobile design house, we also know this is what the clients want, but unfortunately often not what the clients get. The challenge in this case is the [high development cost](https://news.ycombinator.com/item?id=13332501) for mobile apps, let alone mobile apps with beautiful custom UI. + +Therefore, rather than just a cross-platform tool to connect the 2 platforms’ SDKs, the first order of business for the developers of the Flutter framework was to create **the best possible framework available for creating customized mobile designs**. One that most empowers developers to easily express any type of design language, any UI components and any UX suited for their customized solution, across platforms, and with high performance. + +Back then to the topic of seeing how that aspiration relates to the usage or non-usage of OEM UI widgets. This objective translated into the following UI design principles for Flutter: + +### 1. Declarative API + +While OEM toolkit views implement a mutable imperative style of UI API, Flutter is excited and [inspired](https://twit.tv/shows/floss-weekly/episodes/439) by the recent popularity of [new UI programming paradigms](https://youtu.be/KVZ-P-ZI6W4) as a solution to manage the scalability of UI logic as the number of input signals affecting the UI output of today’s apps increases in both number and complexity. As such, Flutter uses a [reactive, declarative style UI API](https://hackernoon.com/whats-revolutionary-about-flutter-946915b09514#---0-25). + +This frees up the developer to focus on the higher value-add task of describing the desired end state. It leaves the more arduous, error-prone task of performing the optimized mutations and animations between those states to the framework. + +### 2. Modularity + +To empower the developers to be experience creators rather than assemblers, the Flutter toolkit is built by composition with accessible APIs at every layer. + +The same UI building components were used by the Flutter team to create the higher level ‘skinned’ UI widgets, such as Material Design widgets. And these components are designed to be consumable, replaceable, tweakable at every level by the developers. In other words, we bundled ‘skins’ that are high fidelity but not special in privilege. And while building them, we created a toolset that makes it easy to build any UI style and widget. + +It also means there is no hidden magic anywhere. You can go-to-definition through the framework stack in your IDE, read, modify and have the modified app or framework behaviors hot-reloaded into a live running app on a phone in sub-second time! + +### 3. Predictable consistency + +The customer is always right. In other words if the app creator, the domain expert of the problem he/she is trying to solve, has already decided on the look and feel of the solution then that solution is consistently, predictably and exactly what the developer and their users should get. And that should be true regardless of compat library versions, regardless of platform, regardless of OS versions, regardless of phone manufacturer skinning(unless the developer wants them to be different). + +As is noticeable in many of the example apps’ screenshots above, despite the developers’ seeming attempts to create the same interface on both platforms by wrangling very different toolsets and by possibly different teams, many minor discrepancies nevertheless exist. While their users won’t be comparing the app on each platform and version side by side and pixel by pixel, behavioral and visual divergences can nevertheless have a productivity cost in their future fragility to modifications or bugs. + +Meanwhile predictable consistency in outputs can increase development confidence and speed. + +### 4. Don’t surprise the user + +We might call the previous section “don’t surprise the developer”. Though it’s just as important as “don’t surprise the users” as well. + +In other words, emphasize that it’s important to acknowledge the common native environments’ patterns and offer a seamless experience between the application and the OS to match the users’ expectations and muscle memories. + +Whether it’s the menu bar placement and modifier key shortcuts on desktop, or the scrolling drag coefficient and typography on mobile, the app should feel naturally at home and not stick out like a sore thumb. + +### 5. Fluid motions + +As current design trend are indicating, motion design and development are treated as a first-class citizen, along with *UI performance*. In Flutter, motion expressivity and performance is deeply embedded in the API design and higher level widgets. + +## How to get there + +With now the established primary goal of making it easy for developers to create any custom UI, we explore some possible implementation options. While various approaches can get us very close, we really wanted to hit all the principles listed above to offer the best workflow and experience. + +[[A good article from Wm](https://hackernoon.com/whats-revolutionary-about-flutter-946915b09514) describing the different approaches’ categorization] + +### Approach 1 — Web technology + +Fixed on solving the mobile development productivity problem for many years, the Flutter team includes many web technology luminaries. So we first investigated the popular ‘[hybrid](https://en.wikipedia.org/wiki/HTML5_in_mobile_devices#Hybrid_Mobile_Apps)’ cross-platform approach. The team spared no expense stripping down various web components to fit the mobile needs. But maintaining the existing compatibility of the web’s extensive flexibility made it too difficult to achieve the necessary performance characteristics, especially with respect to motion when web components are molded to a native look and feel. + +### Approach 2 — Native widget wrapping + +The second approach involved wrapping OEM widgets with another full-featured declarative-style UI API in a common third language. + +We really wanted to offer our users a highly modularized set of tools to facilitate custom experience creation. However, we didn’t have a good way of breaking everything into small modular units that then maps back to large, feature-rich, high-powered, mutable OEM widgets on the 2 platforms. We also weren’t able to achieve the desired motion fluidity or UI consistency by letting lightweight, layered, composable UI units manipulate heavy OEM widgets with very different APIs on 2 platforms via a high traffic interop bridge. + +### Approach 3 — full stack rendering + +In order to fully achieve the all the traits of a framework we think will be the best for expressing mobile UI, the Flutter team experimented further and pushed into more ambitious territories beyond reusing OEM widgets. + +At the bottom, for solid battle-tested consistency, Flutter started with [Skia](https://skia.org/), a very mature graphics engine used to render Chrome, Firefox and Android’s low-level graphics. By rendering directly via OpenGL or Vulkan GPU instructions, this ensures any UI built by a framework on top of it look predictable and consistent across different runtime environments. + +On top of this reliable and efficient drawing functionality, we designed and created our desired modular UI building toolset with the right level of granularity and developer control. + +And because this toolset is written in [Dart](https://www.dartlang.org/) that’s not only expressive but also entirely compiles down to ARM code, its performance characteristics match apps built on OEM SDKs. + +<DashImage figure src="images/1RLKlDP0xpOdbQYkdzNtVFw.jpeg" alt="Performance is literally right in our faces as we develop the framework" caption="Performance is literally right in our faces as we develop the framework" /> + + +Because Dart is both expressive and fast, it even allowed us to further empower our layered modularity principle by preserving as much composable UI building blocks accessible on the Dart side as possible vs the underlaying C++ side. For instance, the [layout mechanisms](https://github.com/flutter/flutter/blob/master/packages/flutter/lib/rendering.dart) are developer customizable whereas they were buried deeper in the inaccessible C++ side for web browsers. + +And it is on this foundation that Flutter tied it all together with a declarative API pattern. Dart is also especially optimized for this design pattern because of its cheap instantiation costs of immutable virtual UI trees. + +Then, using this UI building API, we created a batteries-included set of rich [Material Design](https://flutter.io/widgets/material/) and [Cupertino (iOS)](https://flutter.io/widgets/cupertino/) widgets to get developers up and running quickly without compromising their future ability to get deeper and customize. + +We also made sure these widgets (both included ones and the ones you create) easily conforms to platform conventions. For instance, in no particular order: + +* Scrolling and overscrolling have different tightly modeled physics simulations between Android and iOS + +* Components like app navigation bars (either skinned or unskinned) by default match the text alignment and paddings etc of the OS + +* Page transitions’ animation curves, directions, durations, opacity changes etc match the OS + +* Platform specific behaviors like edge swipe to go back, status bar tap to scroll up on iOS and tap outside dialogs to dismiss on Android work as expected + +<YoutubeEmbed id="Zqv160V-oLo" title="Flutter's platform UI adaptations" fullwidth="true"/> + + +With this full stack rendering approach, Flutter is able to deliver a UI building experience without compromising on performance, expressivity, flexibility, or consistency. And, by fulfilling these principles, we think we’ve created a tool that best allows developers to create customized UIs like those award-winning apps presented in the earlier screenshots. + +## Design implications + +Given now these design choices of the Flutter framework, let’s look forward again and evaluate their other exciting impacts for the Flutter developers. + +### Development speed + +Thanks to the declarative and reactive UI style, and Dart’s properties, Flutter apps is hot-reloadable and can be reloaded quickly and without losing application state. Because of the declarative style’s central ability to interpolate between desired resting states, hot-reload works even in the middle of animations or other transient tasks whose present state is unknown to the explicit user program. + +Sub-second development cycles drastically reduces UI development costs and can eliminate the need for a [separate prototyping tool](https://www.youtube.com/watch?v=BJCqRpvvTrM) or a static layout builder tool since your production UI is the output feedback. + +Not only it was important to save the waiting time for the rebuild, it was doubly impactful to let the developer continue the task within [human attention span](https://www.nngroup.com/articles/response-times-3-important-limits/) to prevent [context switching](https://blog.codinghorror.com/the-multi-tasking-myth/). I certainly lost count of the number of times in the past that I went for a snack run while a fix was being rebuilt into the app and never again returned to the same task that day. + +This is even more important as the app complexity increases and certain UI bugs only surface through some non-trivial repro steps. Because of Flutter’s separation of UI description from UI state, UI and behaviour can be reloaded and retested while preserving the existing runtime state. + +### Testability + +A UI toolkit built by composition is **inherently more testable**. Semantic UI testing have always been a huge headache for native mobile developers using a variety of tacked-on mechanisms like [EarlGrey](https://github.com/google/EarlGrey), [Espresso](https://developer.android.com/training/testing/ui-testing/espresso-testing.html), [UI Automator](https://developer.android.com/training/testing/ui-automator.html), [KIF](https://github.com/kif-framework/KIF) etc. Even then, you can only write unwieldy, flaky top-level integration tests that are costly to run in both time and resource because they require a full application runtime (which again triggering the dreaded context switch when doing test driven development). + +Flutter, by contrast, has modular [UI testing](https://flutter.io/testing/) built “batteries-included” into the framework. It lets you drive, test and assert UI logic at every level such as layout, gestures, animations, text layouts, colors etc. And most importantly, it runs at [TDD](https://en.wikipedia.org/wiki/Test-driven_development) speed because it can run in headless mode without any device or simulators. + +On top of hot-reload time savings, for the first time, I can write UI code and check the correct UI look and behavior without ever looking at any UI via tests! And this is possible because we draw our own widgets and codified various layers of this widget drawing as public APIs. + +### Developer control + +And I felt necessary to mention developer control again because this has bitten me so frequently in the past :) + +All UI toolkit and framework developers strive to make the perfect product, as much as possible. But everyone makes mistakes or fails to foresee a particular use case. Sometimes it’s one use case that you unfortunately landed on, and no matter how much you upvote that new API feature request or bug fix, you won’t get it before your product is due to ship. + +In other words, we’re all bound to hit the “that widget is perfect but if only I can change the border radius there” or the “this view group thing is great if only I can change where it folds onto the next row” moment at some point. + +With Flutter, we built the framework knowing we can’t perfectly predict future market directions with centralized 5-year economic plans, so to speak. So all parts of the UI building framework can be clicked into, read without having to cross-reference your OS version, modified live while your app is running and have the change hot-reloaded into the running app. + +This way, developers don’t have any API walls they can hit that limits their creativity. And those ‘extravagant design ambitions’ from the design team no longer need to be bound (we hope) by the wise, pragmatic engineer who points out that certain parts of the mockup have exponentially higher implementation costs, and need to be cut to meet the schedule. + +And this is also possible not only because of Flutter’s modular design, but again owns much to Dart’s compile-to-native performance which allowed us to elevate previously inaccessible parts of the UI framework such as the physics simulation and the layout engine out of deeper C++ code into easy-to-use and easy-to-modify Dart code. + +### Environment decoupling + +Flutter decouples the UI toolkit from the OS. + +By shipping the natively compiled framework in user space, the user’s experience is predictable, inherently stable and controlled by the developer — not by the user’s phone, the OS version, the phone manufacturer’s UI skins, compat library versions etc, unless the developer chooses so. + +Material Design can be implemented with as much fidelity on an early iOS 11 beta as it can be on a 2010 Nexus S (released 4 years before Material Design is even created!) on Android 4.1.2. Not that we necessarily encourage anyone to do it. + +<DashImage figure src="images/1G2Bl4jOqWjj1s3ojNLgERQ.webp" /> + + +<DashImage figure src="images/1t3UOazDW4es-wlm5WhHlLw.webp" alt="Consistent Material Design (or your design) on phones made way before Material Design existed" caption="Consistent Material Design (or your design) on phones made way before Material Design existed" /> + + +Correspondingly, we can not only go back 7 years and ship the native iOS 11 beta look and feel on a 2010 Android device, we also made it easy enough to create any UI that we could do so before iOS 11 itself released. Again, not that anyone would want to make an iOS looking app on Android, but we hoped to use it as a litmus to make sure it’s easy to create any UI, whether stock or branded. + +<DashImage figure src="images/1mWZiUb3dO9aMN1iV8YFxCw.webp" alt="iOS 11 on an old, single-core 1GHz Android phone before iOS 11 came out" caption="iOS 11 on an old, single-core 1GHz Android phone before iOS 11 came out" /> + + +## Conclusion + +While there are many ways of creating a UI framework, I gradually learned that Flutter’s goal of creating the best tool to build tailored UI experiences led the team through a number of conceptual iterations that ended with the version we have today. + +Through this exploration, I quelled my initial skeptical reflex about Flutter’s non-use of OEM widgets. Rather I’m now excited about the potentials it unlocked and how it aligned with the principles we identified for creating the best UI building framework. + +I believe Flutter’s approach can drastically lower the barrier of entry for the creation of beautiful award-winning mobile apps while making it more enjoyable for the developers. And I’m excited to see what people can create with it. So far, our users’ experiences corroborate with our expectations that we can help users create beautiful applications for both platforms at a cost lower than the cost of creating the same application for a single platform using the platform’s native SDKs. + +Give it a spin and see for yourself at [flutter.io](https://flutter.io) and give us your thoughts on our [chat channel](https://gitter.im/flutter/flutter) or on [GitHub](https://github.com/flutter/flutter)! \ No newline at end of file diff --git a/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1H2OK-1btsNZfUcu57PTsPQ.webp b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1H2OK-1btsNZfUcu57PTsPQ.webp new file mode 100644 index 0000000000..5a08310ba5 Binary files /dev/null and b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1H2OK-1btsNZfUcu57PTsPQ.webp differ diff --git a/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1KfSxgSjmEBFAJQwExa18hA.jpeg b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1KfSxgSjmEBFAJQwExa18hA.jpeg new file mode 100644 index 0000000000..50e68a504c Binary files /dev/null and b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1KfSxgSjmEBFAJQwExa18hA.jpeg differ diff --git a/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1PF95FgsvYP71V5_5c0DMpA.gif b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1PF95FgsvYP71V5_5c0DMpA.gif new file mode 100644 index 0000000000..d2fe770563 Binary files /dev/null and b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1PF95FgsvYP71V5_5c0DMpA.gif differ diff --git a/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1TLJOrUTMNXDYLtEHhpk0fg.jpeg b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1TLJOrUTMNXDYLtEHhpk0fg.jpeg new file mode 100644 index 0000000000..746cb0d9dd Binary files /dev/null and b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1TLJOrUTMNXDYLtEHhpk0fg.jpeg differ diff --git a/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1UNPFvzQCMU0xbqqbNXUAcw.webp b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1UNPFvzQCMU0xbqqbNXUAcw.webp new file mode 100644 index 0000000000..f160423ffc Binary files /dev/null and b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1UNPFvzQCMU0xbqqbNXUAcw.webp differ diff --git a/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1am1D-P1vH_f6y1Q-9dgCdw.webp b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1am1D-P1vH_f6y1Q-9dgCdw.webp new file mode 100644 index 0000000000..c972625227 Binary files /dev/null and b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1am1D-P1vH_f6y1Q-9dgCdw.webp differ diff --git a/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1bRHZis_QLJmRF-ieQUVlCg.jpeg b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1bRHZis_QLJmRF-ieQUVlCg.jpeg new file mode 100644 index 0000000000..48f171e275 Binary files /dev/null and b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1bRHZis_QLJmRF-ieQUVlCg.jpeg differ diff --git a/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1khKciHLLdyG1gIY3qmwbLw.webp b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1khKciHLLdyG1gIY3qmwbLw.webp new file mode 100644 index 0000000000..97aefc1950 Binary files /dev/null and b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1khKciHLLdyG1gIY3qmwbLw.webp differ diff --git a/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1qWrX4tkqykAQHW3kSkuo8g.webp b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1qWrX4tkqykAQHW3kSkuo8g.webp new file mode 100644 index 0000000000..5831df4304 Binary files /dev/null and b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/images/1qWrX4tkqykAQHW3kSkuo8g.webp differ diff --git a/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/index.md b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/index.md new file mode 100644 index 0000000000..075a64c4e6 --- /dev/null +++ b/sites/www/content/blog/why-you-should-smoke-test-your-sample-code-and-how-to-do-it-in-flutter/index.md @@ -0,0 +1,262 @@ +--- +title: "Why you should smoke-test your sample code, and how to do it in Flutter" +description: "[ If you’re already convinced that you want to smoke-test your Flutter samples, you can skip to “Smoke-testing Flutter” below. What follows…" +publishDate: 2018-06-06 +author: filiph +image: images/1khKciHLLdyG1gIY3qmwbLw.webp +category: tutorial +layout: blog +--- + +*[ If you’re already convinced that you want to smoke-test your Flutter samples, you can skip to “Smoke-testing Flutter” below. What follows is a general guideline applicable to any technology. ]* + +<DashImage figure src="images/1khKciHLLdyG1gIY3qmwbLw.webp" /> + + +When learning any new technology, sooner or later developers want to look at sample code. Not just a few lines, either. Often, they want to see a whole project and how it’s assembled. + +That, by itself, means that any library, framework or SDK needs to maintain a number of sample apps. But there’s more. + +Samples are most useful when they’re focused. A classic mistake is to ram everything-there-is-to-know into a single sample app. That’s easier for the author (only one app to create and maintain) but unhelpful for the newcomer. Nobody learns *everything at once*. + +With any sufficiently-complex technology, the developer first wants to see some code sample of the basics (“hello world”), then maybe a small app (TodoMVC, HN app), and then focused samples according to their needs (networking, parallelism, animation, etc.). The idea is that they never need to try and comprehend multiple hard concepts at once. + +(Yes, very complex, real-world samples are valuable. But, generally speaking, not for beginners or intermediate developers. They are great for when you already know much of the technology, so you can navigate your way around the complex sample, and identify the parts that are relevant to you at the time.) + +For the author of the samples, that means a lot of self-contained little apps. Which in turn means many code repositories. And any of these can (and will) get out of date, sooner or later. + +<DashImage figure src="images/1TLJOrUTMNXDYLtEHhpk0fg.jpeg" /> + + +You don’t want sample code to go out of date. For a developer, there are few things more frustrating than getting excited about a new technology, trying to run a sample app, and seeing it fail. + +So, to summarize: + +* A good sample coverage calls for a lot of independent sample apps. For the sake of argument, let’s say we have **100** independent sample apps. + +* Every single sample app needs to be kept maintained. If there is any major breakage coming down the line, we should know about this as soon as possible. + +What to do? Here are some options: + +### Do nothing + +Errors in samples get caught by the community sooner or later, but by the time you get to know about them, they will have done damage. This does not only lead to frustration of the people who find the breakage, but it will most likely be widely reported sooner or later, leading to damaged reputation of your project. + +You will be shocked (*shocked!*) to learn that this is *by far* the most widely used option for sample repos. + +### Test manually + +Some poor soul is tasked to manually check the 100 samples every time there’s a new version of the underlying technology. + +Many teams do this, and you can probably imagine how that goes, or at least how that keeps the pressure on having significantly *less* than 100 samples. + +### Perform static analysis in Continuous Integration (CI) + +For strongly typed languages, this is already way better than the previous option. But static analysis doesn’t catch all breakages. An app with no static errors can still fail to build, or crash at runtime. + +<DashImage figure src="images/1bRHZis_QLJmRF-ieQUVlCg.jpeg" /> + + +### Full unit testing in CI + +Unit testing is indispensable, but not a good tool for this particular job. Most code samples have trivial logic (e.g. “this function always returns 42”) — not really worth testing. Also, few technologies change in a way that breaks your individual *unit* tests — so it’s not really what you want to test. You want to test that the sample app, when put together, works. + +But of course, if you can spare the resources to unit test everything, awesome. + +### Full functional / integration testing in CI + +This is the best option *if* you have the time and resources. Which—let’s face it—you don’t. Not for 100 different sample repos. + +If you can manage, then this is obviously fantastic. You verify that the app not only statically checks and compiles, but also that it runs in a real-world-ish environment. (While we’re in the mode of dreaming: let’s test the sample apps under all supported operating systems and both on real devices and in emulators.) + +### Put samples directly in the tech’s repository + +When you have your samples developed (and tested) in the same repo as the technology they’re “sampling” (like Flutter does with the [Flutter Gallery](https://github.com/flutter/flutter/tree/master/examples/flutter_gallery)), there is no chance they’ll go out of date. When the underlying technology changes in a way that breaks the sample, the build goes red and the development team is highly motivated to get the example code fixed. + +The problem is that you can’t do this with too many samples. That would slow development. Also, more often than not, you depend with your samples on technologies that you don’t own, so you can’t just put the sample in the same repo. + +## Smoke testing + +The term “smoke test” comes from hardware. It’s the first line of Quality Assurance: “[when you turn a device on, does smoke come out of the device?](https://en.wikipedia.org/wiki/Smoke_testing_(electrical))” If so, there’s no need for more in-depth testing. + +The term has taken on a broader meaning. Smoke tests are, basically, very quick and incomplete functional or integration tests. Some call them sanity checks. + +Smoke tests take your code, build it, run it, and verify a thing or two. They will not verify that your software is 100% functional and correct. In a production app, they will act as a quick bail out — if the smoke test fails, the whole build fails. And it does so quickly. + +My point is that, for a sample app, smoke test is often all you need. + +### Recommendation: + +1. Every sample app, however small, comes with a smoke test. + +1. The smoke test can be very short, but it should open all screens and test at least one major piece of functionality. + +1. The smoke test is performed by your CI tool on every commit to the sample repo (incl. pull requests). + +1. The smoke test is *also* run periodically (e.g. once a week) to check that it’s still in sync with the underlying technology (which may have updated). + +1. The CI runs the smoke test against both the current stable version of the underlying technology, and the development version. (The latter will give you an early warning of coming breakages.) + +1. You (or the team responsible for the sample) subscribe to CI notifications. + +1. You show the CI badge (red or green build) in the sample’s README. + +*[ This marks the end of the general part of this post. Continue only if you care about how to implement this with your Flutter samples, on Travis. ]* + +<DashImage figure src="images/1KfSxgSjmEBFAJQwExa18hA.jpeg" /> + + +## Smoke-testing Flutter sample apps on Travis CI + +The good news is that Flutter provides a really good testing framework: [flutter.io/testing](https://flutter.io/testing/). It has capabilities and convenience features for unit tests, widget tests, and integration tests. + +Given unlimited time and resources, you’d want to use full integration testing. That means running your sample apps on a device, or at least in the iOS Simulator / Android Emulator. Thanks to `FlutterDriver`, that’s [easier than you’d think](https://flutter.io/testing/#writing-integration-tests), but still — it requires you to install an Emulator, run it, connect to it, and so on, for every commit. For most Flutter samples out there, this is overkill. + +Instead, you want to use a *widget* test. Remember: in Flutter, everything is a widget, and that applies for your whole app as well. You can easily widget-test your whole app. Like so: + +``` +testWidgets(**'smoke test'**, (WidgetTester tester) **async** { + **final** app = MyApp(); + **await** tester.pumpWidget(app); + + expect(find.text("0"), findsOneWidget); + + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + expect(find.text("1"), findsOneWidget); +}); +``` + + +This test runs completely headless, and, on my work laptop, it completes in 50 milliseconds. Yet it simulates the whole Flutter framework. + +Just for fun, try to run it as a full app (`flutter run test/smoke_test.dart` instead of the usual `flutter test`), and you’ll see something like this: + +<DashImage figure src="images/1UNPFvzQCMU0xbqqbNXUAcw.webp" alt="These are the smoke tests for all the different architectural flavors of our Google I/O talk sample app, running on device with ``flutter run`` (instead of ``flutter test``). You should be able to see “Redux”, “ValueNotifier”, “ScopedModel” etc. in the title bar up there. I’m showing this instead of the test shown above because that one finishes way too fast, so there’s not much to look at." caption="These are the smoke tests for all the different architectural flavors of our Google I/O talk sample app, running on device with ``flutter run`` (instead of ``flutter test``). You should be able to see “Redux”, “ValueNotifier”, “ScopedModel” etc. in the title bar up there. I’m showing this instead of the test shown above because that one finishes way too fast, so there’s not much to look at." /> + + +This is useful to visualize your test, and to verify that it’s doing what you think it’s doing. + +### .travis.yml + +Now that you have a smoke test, let’s configure Travis CI to watch your repo (getting started instructions are [here](https://docs.travis-ci.com/user/getting-started/)) and add the following `.travis.yml` file to the root of your repository. I’ll explain its contents step-by-step below. But first, let’s see the whole file: + +``` +os: + - linux +sudo: false +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - libstdc++6 + - fonts-droid +git: + depth: 3 +cache: + directories: + - $HOME/.pub-cache +env: + - FLUTTER_VERSION=beta + - FLUTTER_VERSION=dev +matrix: + allow_failures: + - env: FLUTTER_VERSION=dev +before_script: + - git clone [https://github.com/flutter/flutter.git](https://github.com/flutter/flutter.git) -b $FLUTTER_VERSION + - ./flutter/bin/flutter doctor +script: + - ./flutter/bin/flutter test +``` + + +The lines at the top—everything between `os` and `fonts-droid`—are needed for any Flutter testing. [Yegor has a write-up](https://medium.com/flutter-io/test-flutter-apps-on-travis-3fd5142ecd8c) if you want to learn more. + +I tend to add the `git:` depth option — it tells Travis to fetch the latest few git versions of your app, instead of the whole history of your repo. + +Another optional but useful configuration is `cache`. It can make your builds faster while saving resources for the fabulous people at Travis. + +Then, we have this: + +``` +env: + - FLUTTER_VERSION=beta + - FLUTTER_VERSION=dev +``` + + +This tells Travis to run two separate builds: one with the custom variable `FLUTTER_VERSION` set to `beta` (stable builds of Flutter), and one with it set to `dev` (~nightly builds of Flutter). These two builds run in parallel. We’ll use the variable below. + +Next, we allow the `FLUTTER_VERSION=dev` build to fail using Travis’ [build matrix functionality](https://docs.travis-ci.com/user/customizing-the-build#Build-Matrix): + +``` +matrix: + allow_failures: + - env: FLUTTER_VERSION=dev +``` + + +It’s good to know whether our sample works with the bleeding edge version of Flutter, but we don’t want to paint our sample red whenever it fails there. Consider the`FLUTTER_VERSION=dev` build to be an early warning system for breaking changes. It might tell us about things that are coming to Flutter that we should fix, or prepare to fix, in our sample. But there can be false positives here—it’s a bleeding edge channel, after all. + +Next, we actually use the `$FLUTTER_VERSION` variable when installing Flutter: + +``` +before_script: + - git clone [https://github.com/flutter/flutter.git](https://github.com/flutter/flutter.git) -b $FLUTTER_VERSION +``` + + +Git fetches the branch specified with `-b`, which in our case is either `beta` or `dev`. + +Still in `before_script`, we run `flutter doctor`. This is to make sure the Flutter installation is sound. + +``` + - ./flutter/bin/flutter doctor +``` + + +Note that we didn’t add the `flutter` tool to `$PATH`, so we’re accessing it via the full path. That’s just a matter of personal preference. + +Lastly, we actually run the test: + +``` +script: + - ./flutter/bin/flutter test +``` + + +Nice! You can commit the `.travis.yml` file to your repository, push it to github, head over to [travis-ci.org](https://travis-ci.org/), and voilà: + +<DashImage figure src="images/1PF95FgsvYP71V5_5c0DMpA.gif" alt="Notice the two jobs, one of which is “Allowed Failure”." caption="Notice the two jobs, one of which is “Allowed Failure”." /> + + +The build runs for about 2 minutes, most of which is installation (the test itself takes a few seconds at most). + +### Run the test periodically + +Now, we don’t only want the build to run on every commit or PR (the default with Travis+GitHub) but also periodically. After all, most of the time when samples get broken, it’s not because someone changed them, but because the underlaying technology changed under them. + +We can use Travis’ [“Cron Job” feature](https://docs.travis-ci.com/user/cron-jobs/). Just head over to [travis-ci.org](https://travis-ci.org/) and set it up. + +<DashImage figure src="images/1H2OK-1btsNZfUcu57PTsPQ.webp" /> + + +### Set up notifications + +By default, Travis will send you an email any time the build fails. But I highly recommend to [check the settings for these notifications](https://docs.travis-ci.com/user/notifications/). Not only can you make sure that the right person or team is notified, but you can also set up fancy stuff like IRC or Slack alerts. + +### Add badge + +Last but not least, add the build badge to your sample’s README.md. Head over to Travis again, and click on the badge icon. It’ll give you the code to use. + +<DashImage figure src="images/1qWrX4tkqykAQHW3kSkuo8g.webp" /> + + +Paste this to the top of your README.md. Then commit, push, and behold: + +<DashImage figure src="images/1am1D-P1vH_f6y1Q-9dgCdw.webp" /> + + +Congratulations. Your sample code is now smoke tested. \ No newline at end of file diff --git a/sites/www/content/blog/wonderous-explore-the-world-with-flutter/images/1Tb-JXZnxo2pu232C4c2KqQ.webp b/sites/www/content/blog/wonderous-explore-the-world-with-flutter/images/1Tb-JXZnxo2pu232C4c2KqQ.webp new file mode 100644 index 0000000000..be1d7eb110 Binary files /dev/null and b/sites/www/content/blog/wonderous-explore-the-world-with-flutter/images/1Tb-JXZnxo2pu232C4c2KqQ.webp differ diff --git a/sites/www/content/blog/wonderous-explore-the-world-with-flutter/images/1UhID-wmcu6aSotxCJjGVtQ.webp b/sites/www/content/blog/wonderous-explore-the-world-with-flutter/images/1UhID-wmcu6aSotxCJjGVtQ.webp new file mode 100644 index 0000000000..7f0463d125 Binary files /dev/null and b/sites/www/content/blog/wonderous-explore-the-world-with-flutter/images/1UhID-wmcu6aSotxCJjGVtQ.webp differ diff --git a/sites/www/content/blog/wonderous-explore-the-world-with-flutter/index.md b/sites/www/content/blog/wonderous-explore-the-world-with-flutter/index.md new file mode 100644 index 0000000000..6890b958f0 --- /dev/null +++ b/sites/www/content/blog/wonderous-explore-the-world-with-flutter/index.md @@ -0,0 +1,42 @@ +--- +title: "Wonderous: Explore the World with Flutter" +description: "A reference app that shows how to develop tailored UI using Flutter" +publishDate: 2022-08-30 +author: leighajarett +image: images/1Tb-JXZnxo2pu232C4c2KqQ.webp +category: spotlight +layout: blog +--- + +<YoutubeEmbed id="6Hb3QiH_yps" title="Wonderous App, built with Flutter" fullwidth="true"/> + + +Our vision for Flutter is to provide a blank canvas for you to design and paint beautiful apps without limitation. Recently, we teamed up with the amazing group at gskinner to make this vision a reality, by creating a new mobile app, [Wonderous](http://wonderous.app). Wonderous uses Flutter to create a highly expressive user interface, tailored to the mobile form factor, and doesn’t conform to a stock design system. Instead, Wonderous realizes the creative vision of its designer. + +<DashImage figure src="images/1Tb-JXZnxo2pu232C4c2KqQ.webp" alt="Screenshots from the Wonderous App" caption="Screenshots from the Wonderous App" /> + + +### When developing Wonderous, we had a few goals in mind: + +1. **Create an interesting and useful app.** Wonderous isn’t just a demonstration, it’s a fully functional app that allows users to navigate the intersection of history, art, and culture by exploring the Wonders of the World. At a time when travel is difficult for many, Wonderous leverages stunning illustrations and amplified animations to bring the wonder of these sites to life directly on your device. + +1. **Showcase high-quality UX.** We wanted Wonderous to be comparable in quality to any app in the system framework. Wonderous captures the audience with its tailored, imaginative design, while using familiar gestures and behaviors to ensure a seamless user experience. + +1. **Create an app for testing and benchmarking.** We’re already using Wonderous to test Impeller, the new rendering engine that is now available in preview on iOS. We’ve been pleased with the results so far, which have helped validate our hypotheses for improved performance. The version of the app available for download from the Apple App Store uses Impeller, and we’re eager to see how it performs on a broader variety of iOS devices in real-world conditions. + +1. **Provide a reference, open-source example app that demonstrates best practices.** The [Wonderous code](https://github.com/gskinnerTeam/flutter-wonderous-app) features development best practices for custom design, engaging interactions, and novel animations — all while keeping accessibility top of mind. + +1. **Create a source for learning materials and documentation.** Over the course of the next few weeks, the gskinner team will be launching more content surrounding Wonderous — including details on how the animations were created, how accessibility was incorporated, and best practices for app performance. Stay tuned! + +<DashImage figure src="images/1UhID-wmcu6aSotxCJjGVtQ.webp" /> + + +After [downloading Wonderous](http://wonderous.app) from the Apple App Store or the Google Play Store there are a few characteristics you might want to explore: + +* Transitions that would likely be difficult without Flutter, these are especially noticeable when swiping left to right between Wonders + +* Unique scroll patterns as you move through the global timeline. + +* Tailored buttons and icons that match the Wonderous design system. + +We’re so excited to share Wonderous with the world, let us know what you think! \ No newline at end of file diff --git a/sites/www/content/blog/wonderous-nominated-for-webby-award/images/1XksvK4Wq0YbZoIG8udUShw.webp b/sites/www/content/blog/wonderous-nominated-for-webby-award/images/1XksvK4Wq0YbZoIG8udUShw.webp new file mode 100644 index 0000000000..5478092265 Binary files /dev/null and b/sites/www/content/blog/wonderous-nominated-for-webby-award/images/1XksvK4Wq0YbZoIG8udUShw.webp differ diff --git a/sites/www/content/blog/wonderous-nominated-for-webby-award/index.md b/sites/www/content/blog/wonderous-nominated-for-webby-award/index.md new file mode 100644 index 0000000000..3a7070f8af --- /dev/null +++ b/sites/www/content/blog/wonderous-nominated-for-webby-award/index.md @@ -0,0 +1,28 @@ +--- +title: "Wonderous nominated for Webby Award" +description: "Wonderous has been nominated for a Webby award in the Best User Interface category." +publishDate: 2023-04-06 +author: leighajarett +image: images/1XksvK4Wq0YbZoIG8udUShw.webp +category: spotlight +layout: blog +--- + +**You can vote for Wonderous on the [Webby awards website](https://vote.webbyawards.com/PublicVoting#/2023/apps-dapps-and-software/app-features/best-user-interface).** + +In August, we partnered with the [gSkinner](https://gskinner.com/) team to launch [Wonderous](http://wonderous.app) — a mobile app that demonstrates Flutter at its best. Since its launch, over 30 thousand users have installed Wonderous. Users rated it with 5 stars in the App Store. + +<YoutubeEmbed id="6Hb3QiH_yps" title="Wonderous App, built with Flutter" fullwidth="true"/> + + +Wonderous has been nominated for Best User Interface for Apps, dApps and Software in the 27th Annual Webby Awards. The New York Times hails the Webby Awards as the “Internet’s highest honor”. The International Academy of Digital Arts and Sciences (IADAS), the leading international awards organization honoring excellence on the Internet, presents the Webby Awards each year. +> “Nominees are setting the standard for innovation and creativity on the Internet,” said Claire Graves, President of The Webby Awards. “It is an incredible achievement to be selected among the best from the nearly 14,000 entries we received this year.” + +<DashImage figure src="images/1XksvK4Wq0YbZoIG8udUShw.webp" /> + + +With beautiful graphics and unique animations, Wonderous lets you experience the wonders of the world straight from your device. Wonderous uses Flutter to create an expressive user interface that tailors to the mobile form factor and doesn’t conform to a stock design system. Instead, it realizes the creative vision of its designer. + +You can download Wonderous from the [App Store](https://apps.apple.com/us/app/wonderous/id1612491897) or[ Play Store](https://play.google.com/store/apps/details?id=com.gskinner.flutter.wonders&hl=en_US&gl=US). You can [read about its best practices](http://wonderous.app) in adaptive design, animation, performance and accessibility, or inspect the [complete source code.](https://github.com/gskinnerTeam/flutter-wonderous-app) + +As a nominee, Wonderous could also win a Webby People’s Voice Award. This would be the online vote of fans across the globe. From now until April 20, Wonderous fans and Flutter developers can cast their votes at [the Webby Awards website](https://vote.webbyawards.com/PublicVoting#/2023/apps-dapps-and-software/app-features/best-user-interface). \ No newline at end of file diff --git a/sites/www/content/blog/writing-a-good-code-sample/images/0-1m66Jxnec3s8Qnb.webp b/sites/www/content/blog/writing-a-good-code-sample/images/0-1m66Jxnec3s8Qnb.webp new file mode 100644 index 0000000000..e2a37ac271 Binary files /dev/null and b/sites/www/content/blog/writing-a-good-code-sample/images/0-1m66Jxnec3s8Qnb.webp differ diff --git a/sites/www/content/blog/writing-a-good-code-sample/index.md b/sites/www/content/blog/writing-a-good-code-sample/index.md new file mode 100644 index 0000000000..d211bfe541 --- /dev/null +++ b/sites/www/content/blog/writing-a-good-code-sample/index.md @@ -0,0 +1,38 @@ +--- +title: "Writing a good code sample" +description: "Authoring a good code sample is hard. Let me clarify that, putting together a sample that demonstrates the usage of an API, or shows off a…" +publishDate: 2021-08-23 +author: domesticmouse +category: spotlight +layout: blog +--- + +Authoring a good code sample is hard. Let me clarify that, putting together a sample that demonstrates the usage of an API, or shows off a UI idiom, can be quick and easy. The difficulty comes from the additional pieces you need to add to your repository to keep the code sample fresh and appealing to your developers. + +The first thing to add to your code is a good README that starts with a summary of why a developer should invest the time and effort to investigate and understand the sample. Following that with usage instructions, and links to an issue tracker where developers can raise bugs and ask for clarification if something is confusing is a great next step. + +Next up, the code itself. Does the code follow conventions for layout and usage? This may be as simple as where does the code go, or as intricate as appropriate settings for linters and the like. I like programming community languages that have adopted a strong standard as to code formatting, layout, and linting rule sets. These standards bring conformity to the look and feel of the code which make it easier to navigate. To enforce code formatting for Dart and Flutter you can add the following command to your CI pipeline to fail the build if it isn’t correctly formatted: + +``` +$ dart format --output none --set-exit-if-changed . +``` + + +After code formatting, the next step is enforcing a good set of lints. For Dart, I strongly recommend investigating the [lints](https://pub.dev/packages/lints) package, and for Flutter I likewise recommend the [flutter_lints](https://pub.dev/packages/flutter_lints) package. To make sure the lints pass in the CI pipeline, add the following command: + +``` +$ dart analyze +``` + + +Tests. Oh so many tests. Unit tests, integration tests, and for Flutter we also have Widget tests. Tests are great for samples, as the tests communicate intent of how a piece of code is intended to be used. The tests also keep a codebase evergreen, when used in combination with the aforementioned CI pipeline. A good place to learn more about Flutter’s testing capability is the [How to test a Flutter app codelab](https://codelabs.developers.google.com/codelabs/flutter-app-testing). The CI commands for Dart and Flutter respectively: + +``` +$ flutter test # for Flutter projects +$ dart test # for pure Dart projects +``` + + +If your code repository is hosted on GitHub, then I suggest you use the [Dart Setup](https://github.com/marketplace/actions/setup-dart-sdk) action if you have a Dart project, and the [Flutter Action](https://github.com/marketplace/actions/flutter-action) if you have a Flutter project. For additional points, consider adding the [Very Good Coverage](https://github.com/marketplace/actions/very-good-coverage) action to keep test coverage high. Given you’ve gone this far, you probably should advertise your CI status with a [workflow status badge](https://docs.github.com/en/actions/managing-workflow-runs/adding-a-workflow-status-badge). + +Hopefully the above list of additions guides you through making your code sample much more valuable to your target audience. \ No newline at end of file diff --git a/sites/www/content/blog/writing-a-good-flutter-plugin/images/0-1m66Jxnec3s8Qnb.webp b/sites/www/content/blog/writing-a-good-flutter-plugin/images/0-1m66Jxnec3s8Qnb.webp new file mode 100644 index 0000000000..e2a37ac271 Binary files /dev/null and b/sites/www/content/blog/writing-a-good-flutter-plugin/images/0-1m66Jxnec3s8Qnb.webp differ diff --git a/sites/www/content/blog/writing-a-good-flutter-plugin/index.md b/sites/www/content/blog/writing-a-good-flutter-plugin/index.md new file mode 100644 index 0000000000..ec1ec6072e --- /dev/null +++ b/sites/www/content/blog/writing-a-good-flutter-plugin/index.md @@ -0,0 +1,119 @@ +--- +title: "Writing a good Flutter plugin" +description: "Plugins are essential to the Flutter ecosystem. They allow Flutter developers to interact with libraries of the platform the app is…" +publishDate: 2018-10-03 +author: mehmetf +category: tutorial +layout: blog +--- + +Plugins are essential to the Flutter ecosystem. They allow Flutter developers to interact with the platform the app is running on. + +As of October 2018, there are [hundreds of](https://pub.dartlang.org/flutter/packages) plugins available on pub. It is very important for plugins to be of high quality, regardless of whether they come from Google or outside contributors. This article explores what makes a good plugin and how to write one. + +## Think functionality first, then APIs + +Often when writing a plugin, you are trying to use an existing native library. The natural thing to do is to look at that library’s API and expose Dart equivalent API for it. Problems arise when APIs don’t match across platforms and you are forced to expose an intersection of the API often with crippling consequences… + +When writing plugins, *think about functionality first, not APIs*. For instance, if you are writing a local storage plugin, you probably need APIs that support storing values of various types along with an ability to recall them. After you established what kind of functionality you want to provide, then think about your Dart API design: + +```dart +class StoragePlugin { + + /// Reads a string + Future<String> getString(String key) async {} + + /// Writes a string + Future<void> setString(String key, String value) async {} +} +``` + +Only after you have the Dart API, start looking at the public API and see your API calls need to be adapted to the platform specific library API calls: + +```dart +class StoragePlugin { + + Future<String> getString(String key) async { + if (Platform.isIos) { + return await callMethodChannel('fetchValue', {'id': base64encode(key)}); + } else if (Platform.isAndroid) { + return await callMethodChannel('fetchValue', {'id': key}); + } + } + + Future<void> setString(String key, String value) async { + if (Platform.isIos) { + await callMethodChannel('setValue', {'id': base64encode(key), 'value': value}); + } else if (Platform.isAndroid) { + await callMethodChannel('setValue', {'id': key, 'value': value}); + } + } +} +``` +> If there’s a functionality you would like to provide which is not supported by one platform, it is OK for that API to **gracefully** fail for that platform. +> You should not crash the app and the behavior should be thoroughly documented. You should also request the missing functionality from the platform so your plugin can be complete. + +## Avoid platform-specific API methods + +Flutter is platform agnostic so it is out of character for Flutter apps to be forced to write platform specific code. If the plugin user is doing the following in their application: + +``` +if (Platform.isIos) { + myPlugin.doIOSThing(); +} else if (Platform.isAndroid) { + myPlugin.doAndroidThing(); +} +``` + + +consider moving the platform specific logic to the plugin itself so that, they can write: + +``` +myPlugin.doThing(); +``` + + +## Avoid supporting only one platform + +You might be tempted to start small and write a plugin for a functionality you want just for Android. Unfortunately, when the plugin is published, this creates confusion for plugin users when their app misbehaves on unsupported platforms. +> There are counterexamples to this such as [Android Intent](https://github.com/flutter/plugins/tree/master/packages/android_intent). Intents are applicable only on Android so it is OK to expose an Android-only plugin for them. + +## Make your plugin easy to navigate and test + +As a rule of thumb, your plugin should contain mostly Dart code. + +* Native layer is non-trivial to test since it typically deals with libraries that do not function on emulators. + +* Flutter is mostly written in Dart. Flutter apps are *entirely* written in Dart. When tracing code, it would be nice if the majority of the logic can be derived from Dart. + +The only reason why you would need native layer is if the implementation on that platform requires you to keep state and/or do processing. One good example is biometric authentication which has its own complex lifecycle on Android that needs to be managed on the native side. + +*If you need to write extensive native code, consider moving the functionality to individual classes that can be unit tested.* + +Ideally, your native layer will simply listen for method calls, forward them to an existing library and pipe the results back to Dart. If you are doing anything more than that, try to see if it can be moved to Dart side. You will end up writing less code that way since the logic will likely need to be repeated across several platforms. + +## Avoid writing static (or global) methods + +It is tempting to expose just: + +``` +Future<User> authenticate() async { + // Some code +} +``` + + +However, prefer: + +``` +class AuthenticatePlugin { + Future<User> authenticate() async { + // Some code + } +} +``` + + +which makes the plugin mock or fake friendly. Users can easily swap out the real implementation so their apps work on an emulator or a test harness. + +Plugins are one of the best ways to enhance functionality of Flutter. If you have more ideas on plugin best practices, please share via comments. Happy coding! \ No newline at end of file diff --git a/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/15ggIsPDAwb8sAgPw8vZkyw.webp b/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/15ggIsPDAwb8sAgPw8vZkyw.webp new file mode 100644 index 0000000000..4042ab8d7b Binary files /dev/null and b/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/15ggIsPDAwb8sAgPw8vZkyw.webp differ diff --git a/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/19ObVOKbos4DoQsmsqbMnRQ.webp b/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/19ObVOKbos4DoQsmsqbMnRQ.webp new file mode 100644 index 0000000000..07655333c3 Binary files /dev/null and b/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/19ObVOKbos4DoQsmsqbMnRQ.webp differ diff --git a/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1MuSAOLktwY8bTJdPGuoNqA.webp b/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1MuSAOLktwY8bTJdPGuoNqA.webp new file mode 100644 index 0000000000..ade320d7e1 Binary files /dev/null and b/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1MuSAOLktwY8bTJdPGuoNqA.webp differ diff --git a/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1OSc2sFHg8KH4ZQR2ymytKg.webp b/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1OSc2sFHg8KH4ZQR2ymytKg.webp new file mode 100644 index 0000000000..e313e3230d Binary files /dev/null and b/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1OSc2sFHg8KH4ZQR2ymytKg.webp differ diff --git a/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1YiojxPiaWY7lB5v9iZVgDg.webp b/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1YiojxPiaWY7lB5v9iZVgDg.webp new file mode 100644 index 0000000000..9489af96e2 Binary files /dev/null and b/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1YiojxPiaWY7lB5v9iZVgDg.webp differ diff --git a/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1aiUQNf70oukpvNf6sVw3GA.webp b/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1aiUQNf70oukpvNf6sVw3GA.webp new file mode 100644 index 0000000000..be8fcd401f Binary files /dev/null and b/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1aiUQNf70oukpvNf6sVw3GA.webp differ diff --git a/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1dN9og1kRYpRsL-cFIgO23w.webp b/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1dN9og1kRYpRsL-cFIgO23w.webp new file mode 100644 index 0000000000..4d97a2af5e Binary files /dev/null and b/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1dN9og1kRYpRsL-cFIgO23w.webp differ diff --git a/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1kCvpZWFivphnjDnOiIoaIw.webp b/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1kCvpZWFivphnjDnOiIoaIw.webp new file mode 100644 index 0000000000..559370aaab Binary files /dev/null and b/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1kCvpZWFivphnjDnOiIoaIw.webp differ diff --git a/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1qKUFM56S-ZonH1amVDDXTw.webp b/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1qKUFM56S-ZonH1amVDDXTw.webp new file mode 100644 index 0000000000..5d88dfcb75 Binary files /dev/null and b/sites/www/content/blog/zero-to-one-with-flutter-part-two/images/1qKUFM56S-ZonH1amVDDXTw.webp differ diff --git a/sites/www/content/blog/zero-to-one-with-flutter-part-two/index.md b/sites/www/content/blog/zero-to-one-with-flutter-part-two/index.md new file mode 100644 index 0000000000..147a62bb06 --- /dev/null +++ b/sites/www/content/blog/zero-to-one-with-flutter-part-two/index.md @@ -0,0 +1,538 @@ +--- +title: "Zero to One with Flutter, Part Two" +description: "Discovering how to animate composite graphical objects in the context of a cross-platform mobile app. Join an avid concept miner in…" +publishDate: 2017-03-31 +author: mravn +image: images/1OSc2sFHg8KH4ZQR2ymytKg.webp +category: spotlight +layout: blog +--- + +Discovering how to animate composite graphical objects in the context of a cross-platform mobile app. Join an avid concept miner in learning how to apply the tween concept to animation of structured values, exemplified by bar charts. Full code samples, batteries included. + +Edit: updated for Dart 2 on August 8, 2018. [GitHub repo](https://github.com/mravn/charts) and diff links added on October 17, 2018. + +<DashImage figure src="images/1OSc2sFHg8KH4ZQR2ymytKg.webp" /> + + +How do you enter into a new field of programming? Experimentation is obviously key, as is studying and emulating programs written by more experienced peers. I personally like to complement these approaches with concept mining: Trying to work from first principles, identifying concepts, exploring their strength, deliberately seeking their guidance. It is a rationalistic approach which cannot stand on its own, but one that is intellectually stimulating and may lead you to deeper insights faster. + +This is the second and final part of an introduction to Flutter and its widget and tween concepts. At the end of [part one](https://medium.com/dartlang/zero-to-one-with-flutter-43b13fd7b354), we [arrived](https://github.com/mravn/charts/tree/992e11e9cdec5a9fb626d6e4c7b62c0d6c558a9d) at a widget tree containing, among various layout and state-handling widgets, + +* a widget for painting a single `Bar` using custom, animation-aware drawing code, + +* a floating action button widget for initiating an animated change of the bar’s height. + +<DashImage figure src="images/15ggIsPDAwb8sAgPw8vZkyw.webp" alt="Animating bar height." caption="Animating bar height." /> + + +The animation was implemented using a `BarTween`, and I claimed that the tween concept would scale to handle more complex situations. Here in part two, I’ll fulfill that claim by generalizing the design to bars with more properties, and to bar charts containing multiple bars in various configurations. + +Let’s start by adding color to our single bar. We add a `color` field next to the `height` field of the `Bar` class, and update `Bar.lerp` to lerp both of them. This pattern is typical: + +*Lerp between composite values by lerping corresponding components.* + +Recall from part one that “lerp” is a short form of “linearly interpolate” or “linear interpolation”. + +```dart +import 'dart:ui' show lerpDouble; + +import 'package:flutter/animation.dart'; +import 'package:flutter/material.dart'; + +class Bar { + Bar(this.height, this.color); + + final double height; + final Color color; + + static Bar lerp(Bar begin, Bar end, double t) { + return Bar( + lerpDouble(begin.height, end.height, t), + Color.lerp(begin.color, end.color, t), + ); + } +} + +class BarTween extends Tween<Bar> { + BarTween(Bar begin, Bar end) : super(begin: begin, end: end); + + @override + Bar lerp(double t) => Bar.lerp(begin, end, t); +} +``` + +Notice the utility of the static `lerp` method idiom here. Without `Bar.lerp`, `lerpDouble` (morally `double.lerp`), and `Color.lerp` we’d have to implement `BarTween` by creating a `Tween<double>` for the height and a `Tween<Color>` for the color. Those tweens would be instance fields of `BarTween`, initialized by its constructor, and used in its `lerp` method. We’d be duplicating knowledge about the properties of `Bar` several times over, outside the `Bar` class. Maintainers of our code would likely find that less than ideal. + +<DashImage figure src="images/1kCvpZWFivphnjDnOiIoaIw.webp" alt="Animating bar height and color." caption="Animating bar height and color." /> + + +To make use of colored bars in our app, we’ll update `BarChartPainter` to get the bar color from the `Bar`. In `main.dart`, we need to be able to create an empty `Bar` and a random `Bar`. We’ll use a fully transparent color for the former, and a random color for the latter. Colors will be taken from a simple `ColorPalette` which we quickly introduce in a file of its own. We’ll make both `Bar.empty` and `Bar.random` factory constructors on `Bar` ([code listing](https://gist.github.com/mravn-google/90bda9c82df356338b3fe3f733066f6c), [diff](https://github.com/mravn/charts/commit/91c800e7e69f2208afb20535aeeacce5a83b8f01)). + +Bar charts involve multiple bars in various configurations. To introduce complexity slowly, our first implementation will be suitable for bar charts displaying numeric quantities for a fixed set of categories. Examples include visitors per weekday or sales per quarter. For such charts, changing the data set to another week or another year does not change the categories used, only the bar shown for each category. + +We’ll update `main.dart` first this time, replacing `Bar` by `BarChart` and `BarTween` by `BarChartTween` ([code listing](https://gist.github.com/mravn-google/029930ddb613b00b6f5df7179d76fdc4), [diff](https://github.com/mravn/charts/commit/17cb4074be0f8267121ae36d865d9a13393e9e39#diff-fe53fad46868a294b309fc85ed138997)). + +To make the Dart analyzer happy, we create the `BarChart` class in `bar.dart` and implement it using a fixed-length list of `Bar` instances. We’ll use five bars, one for each day of the workweek. We then need to move the responsibility for creating empty and random instances from `Bar` to `BarChart`. With fixed categories, an empty bar chart is reasonably taken to be a collection of empty bars. On the other hand, letting a random bar chart be a collection of random bars would make our charts rather kaleidoscopic. Instead, we’ll choose a random color for the chart and let each bar, still of random height, inherit that. + +```dart +import 'dart:math'; +import 'dart:ui' show lerpDouble; + +import 'package:flutter/animation.dart'; +import 'package:flutter/material.dart'; + +import 'color_palette.dart'; + +class BarChart { + static const int barCount = 5; + + BarChart(this.bars) { + assert(bars.length == barCount); + } + + factory BarChart.empty() { + return BarChart(List.filled( + barCount, + Bar(0.0, Colors.transparent), + )); + } + + factory BarChart.random(Random random) { + final Color color = ColorPalette.primary.random(random); + return BarChart(List.generate( + barCount, + (i) => Bar(random.nextDouble() * 100.0, color), + )); + } + + final List<Bar> bars; + + static BarChart lerp(BarChart begin, BarChart end, double t) { + return BarChart(List.generate( + barCount, + (i) => Bar.lerp(begin.bars[i], end.bars[i], t), + )); + } +} + +class BarChartTween extends Tween<BarChart> { + BarChartTween(BarChart begin, BarChart end) : super(begin: begin, end: end); + + @override + BarChart lerp(double t) => BarChart.lerp(begin, end, t); +} + +class Bar { + Bar(this.height, this.color); + + final double height; + final Color color; + + static Bar lerp(Bar begin, Bar end, double t) { + return Bar( + lerpDouble(begin.height, end.height, t), + Color.lerp(begin.color, end.color, t), + ); + } +} + +class BarTween extends Tween<Bar> { + BarTween(Bar begin, Bar end) : super(begin: begin, end: end); + + @override + Bar lerp(double t) => Bar.lerp(begin, end, t); +} + +class BarChartPainter extends CustomPainter { + static const barWidthFraction = 0.75; + + BarChartPainter(Animation<BarChart> animation) + : animation = animation, + super(repaint: animation); + + final Animation<BarChart> animation; + + @override + void paint(Canvas canvas, Size size) { + void drawBar(Bar bar, double x, double width, Paint paint) { + paint.color = bar.color; + canvas.drawRect( + Rect.fromLTWH(x, size.height - bar.height, width, bar.height), + paint, + ); + } + + final paint = Paint()..style = PaintingStyle.fill; + final chart = animation.value; + final barDistance = size.width / (1 + chart.bars.length); + final barWidth = barDistance * barWidthFraction; + var x = barDistance - barWidth / 2; + for (final bar in chart.bars) { + drawBar(bar, x, barWidth, paint); + x += barDistance; + } + } + + @override + bool shouldRepaint(BarChartPainter old) => false; +} + +``` + +The `BarChartPainter` distributes available width evenly among the bars and makes each bar take up 75% of the width available to it. + +<DashImage figure src="images/1aiUQNf70oukpvNf6sVw3GA.webp" alt="Fixed-category bar chart." caption="Fixed-category bar chart." /> + + +Notice how `BarChart.lerp` is implemented in terms of `Bar.lerp`, regenerating the list structure on the fly. Fixed-category bar charts are composite values for which straightforward component-wise lerping makes sense, precisely as for single bars with multiple properties ([diff](https://github.com/mravn/charts/commit/17cb4074be0f8267121ae36d865d9a13393e9e39)). + +There is a pattern at play here. When a Dart class’s constructor takes multiple parameters, you can often lerp each parameter separately and the combination will look good, too. And you can nest this pattern arbitrarily: dashboards would be lerped by lerping their constituent bar charts, which are lerped by lerping their bars, which are lerped by lerping their height and color. And colors are lerped by lerping their RGB and alpha components. At the leaves of this recursion, we lerp numbers. + +The mathematically inclined might express this by saying that lerping commutes with structure in the sense that for composite values `*C*(*x*, *y*)` we have + +`*lerp*(*C*(*x*1, *y*1), *C*(*x*2, *y*2), *t*) == *C*(*lerp*(*x*1, *x*2, *t*), *lerp*(*y*1, *y*2, *t*))` + +As we have seen, this generalizes nicely from two components (height and color of a bar) to arbitrarily many components (the *n* bars of a fixed-category bar chart). + +There are, however, situations in which this pretty picture breaks down. We may wish to animate between two values that are not composed in quite the same way. As a simple example, consider animating from a bar chart with data for the five days of the workweek to a chart including also the weekend. + +You might readily come up with several different ad-hoc solutions to this problem, and might then go ask your UX designer to choose between them. That’s a valid approach, though I believe it pays to keep in mind during your discussion the fundamental structure common to those different solutions: The tween. Recall from part one: + +*Animate `T`s by tracing out a path in the space of all `T`s as the animation value runs from zero to one. Model the path with a `Tween<T>`.* + +The central question to answer with the UX designer is this: What are the intermediate values between a chart with five bars and one with seven? An obvious choice is to have six bars, but we need more intermediate values than that to animate smoothly. We need to draw bars differently, stepping outside the realm of equal-width, uniformly spaced bars, fitted to 200 pixels. In other words, the space of `T` values must be generalized. + +*Lerp between values with different structure by embedding them into a space of more general values, encompassing as special cases both animation end points and all intermediate values needed.* + +We can do this in two steps. First, we generalize `Bar` to include its *x* coordinate and width as attributes: + +```dart +class Bar { + Bar(this.x, this.width, this.height, this.color); + + final double x; + final double width; + final double height; + final Color color; + + static Bar lerp(Bar begin, Bar end, double t) { + return Bar( + lerpDouble(begin.x, end.x, t), + lerpDouble(begin.width, end.width, t), + lerpDouble(begin.height, end.height, t), + Color.lerp(begin.color, end.color, t), + ); + } +} +``` + +Second, we make `BarChart` support charts with different bar counts. Our new charts will be suitable for data sets where bar *i* represents the *i*th value in some series like sales on day *i* after a product launch. [Counting as programmers](https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html), any such chart involves a bar for each integer value 0..*n*, but the bar count *n* may be different from one chart to the next. + +Consider two charts with five and seven bars, respectively. The bars for their five common categories, 0..5, can be animated compositionally as we’ve seen above. The bars with index 5 and 6 have no counterpart in the other animation end point, but as we are now free to give each bar its own position and width, we can introduce two invisible bars to play that role. The visual effect is that bars 5 and 6 grow into their final appearance as the animation proceeds. Animating in the other direction, bars 5 and 6 would diminish or fade into invisibility. + +*Lerp between composite values by lerping corresponding components. Where a component is missing in one end point, use an invisible component in its place.* + +There are often several ways to choose invisible components. Let’s say our friendly UX designer has decided to use zero-width, zero-height bars with *x* coordinate and color inherited from their visible counterpart. We’ll add a method to `Bar` for creating such a collapsed version of a given instance. + +```dart +class BarChart { + BarChart(this.bars); + + final List<Bar> bars; + + static BarChart lerp(BarChart begin, BarChart end, double t) { + final barCount = max(begin.bars.length, end.bars.length); + final bars = List.generate( + barCount, + (i) => Bar.lerp( + begin._barOrNull(i) ?? end.bars[i].collapsed, + end._barOrNull(i) ?? begin.bars[i].collapsed, + t, + ), + ); + return BarChart(bars); + } + + Bar _barOrNull(int index) => (index < bars.length ? bars[index] : null); +} + +class BarChartTween extends Tween<BarChart> { + BarChartTween(BarChart begin, BarChart end) : super(begin: begin, end: end); + + @override + BarChart lerp(double t) => BarChart.lerp(begin, end, t); +} + +class Bar { + Bar(this.x, this.width, this.height, this.color); + + final double x; + final double width; + final double height; + final Color color; + + Bar get collapsed => Bar(x, 0.0, 0.0, color); + + static Bar lerp(Bar begin, Bar end, double t) { + return Bar( + lerpDouble(begin.x, end.x, t), + lerpDouble(begin.width, end.width, t), + lerpDouble(begin.height, end.height, t), + Color.lerp(begin.color, end.color, t), + ); + } +} +``` + +Integrating the above code into our app involves redefining `BarChart.empty` and `BarChart.random` for this new setting. An empty bar chart can now reasonable be taken to contain zero bars, while a random one might contain a random number of bars all of the same randomly chosen color, and each having a randomly chosen height. But since position and width are now part of the definition of `Bar`, we need `BarChart.random` to specify those attributes too. It seems reasonable to provide `BarChart.random` with the chart `Size` parameter, and then relieve `BarChartPainter.paint` of most of its calculations ([code listing](https://gist.github.com/mravn-google/cac095296074b8b1b7ad6c91a21a5f1a), [diff](https://github.com/mravn/charts/commit/50585bd40160c336e80f3ec867bad01d08d8e0ec)). + +<DashImage figure src="images/1dN9og1kRYpRsL-cFIgO23w.webp" alt="Lerping to/from invisible bars." caption="Lerping to/from invisible bars." /> + + +The astute reader may have noticed a potential inefficiency in our definition of `BarChart.lerp` above. We are creating collapsed `Bar` instances only to be given as arguments to `Bar.lerp`, and that happens repeatedly, for every value of the animation parameter `t`. At 60 frames per second, that could mean a lot of `Bar` instances being fed to the garbage collector, even for a relatively short animation. There are alternatives: + +* Collapsed `Bar` instances can be reused by being created only once in the `Bar` class rather than on each call to `collapsed`. This approach works here, but is not generally applicable. + +* The reuse can be handled by `BarChartTween` instead, by having its constructor create a list `_tween` of `BarTween` instances used during the creation of the lerped bar chart: `(i) => _tweens[i].lerp(t)`. This approach breaks with the convention of using static `lerp` methods throughout. There is no object involved in the static `BarChart.lerp` in which to store the tween list for the duration of the animation. The `BarChartTween` object, by contrast, is perfectly suited for this. + +* A `null` bar can be used to represent a collapsed bar, assuming suitable conditional logic in `Bar.lerp`. This approach is slick and efficient, but does require some care to avoid dereferencing or misinterpreting `null`. It is commonly used in the Flutter SDK where static `lerp` methods tend to accept `null` as an animation end point, typically interpreting it as some sort of invisible element, like a completely transparent color or a zero-size graphical element. As the most basic example, `lerpDouble` treats `null` as zero, unless both animation end-points are `null`. + +The snippet below shows the code we would write following the `null` approach: + +```dart +class BarChart { + BarChart(this.bars); + + final List<Bar> bars; + + static BarChart lerp(BarChart begin, BarChart end, double t) { + final barCount = max(begin.bars.length, end.bars.length); + final bars = List.generate( + barCount, + (i) => Bar.lerp(begin._barOrNull(i), end._barOrNull(i), t), + ); + return BarChart(bars); + } + + Bar _barOrNull(int index) => (index < bars.length ? bars[index] : null); +} + +class BarChartTween extends Tween<BarChart> { + BarChartTween(BarChart begin, BarChart end) : super(begin: begin, end: end); + + @override + BarChart lerp(double t) => BarChart.lerp(begin, end, t); +} + +class Bar { + Bar(this.x, this.width, this.height, this.color); + + final double x; + final double width; + final double height; + final Color color; + + static Bar lerp(Bar begin, Bar end, double t) { + if (begin == null && end == null) + return null; + return Bar( + lerpDouble((begin ?? end).x, (end ?? begin).x, t), + lerpDouble(begin?.width, end?.width, t), + lerpDouble(begin?.height, end?.height, t), + Color.lerp((begin ?? end).color, (end ?? begin).color, t), + ); + } +} +``` + +I think it’s fair to say that Dart’s `?` syntax is well suited to the task. But notice how the decision to use collapsed (rather than, say, transparent) bars as invisible elements is now buried in the conditional logic in `Bar.lerp`. That is the main reason I chose the seemingly less efficient solution earlier. As always in questions of performance vs maintainability, your choice should be based on measurements. + +We have one more step to take before we can tackle bar chart animation in full generality. Consider an app using a bar chart to show sales by product category for a given year. The user can select another year, and the app should then animate to the bar chart for that year. If the product categories were the same for the two years, or happened to be the same except for some additional categories shown to the right in one of the charts, we could use our existing code above. But what if the company had product categories A, B, C, and X in 2016, but had discontinued B and introduced D in 2017? Our existing code would animate as follows: + +``` +2016 2017 + A -> A + B -> C + C -> D + X -> X +``` + + +The animation might be beautiful and silky-smooth, but it would still be confusing to the user. Why? Because it doesn’t preserve semantics. It transforms a graphical element representing product category B into one representing category C, while the one for C goes elsewhere. Just because 2016 B happens to be drawn in the same position where 2017 C later appears doesn’t imply that the former should morph into the latter. Instead, 2016 B should disappear, 2016 C should move left and morph into 2017 C, and 2017 D should appear on its right. We can implement this mingling using one of the oldest algorithms in the book: merging sorted lists. + +*Lerp between composite values by lerping semantically corresponding components. When components form sorted lists, the merge algorithm can bring such components on a par, using invisible components as needed to deal with one-sided merges.* + +All we need is to make `Bar` instances mutually comparable in a linear order. Then we can merge them as follows: + +```dart + static BarChart lerp(BarChart begin, BarChart end, double t) { + final bars = <Bar>[]; + final bMax = begin.bars.length; + final eMax = end.bars.length; + var b = 0; + var e = 0; + while (b + e < bMax + eMax) { + if (b < bMax && (e == eMax || begin.bars[b] < end.bars[e])) { + bars.add(Bar.lerp(begin.bars[b], begin.bars[b].collapsed, t)); + b++; + } else if (e < eMax && (b == bMax || end.bars[e] < begin.bars[b])) { + bars.add(Bar.lerp(end.bars[e].collapsed, end.bars[e], t)); + e++; + } else { + bars.add(Bar.lerp(begin.bars[b], end.bars[e], t)); + b++; + e++; + } + } + return BarChart(bars); + } +``` + +Concretely, we’ll assign each bar a sort key in the form of an integer `rank` attribute. The rank can then be conveniently used also to assign each bar a color from the palette, allowing us to follow the movement of individual bars in the animation demo. + +A random bar chart will now be based on a random selection of ranks to include ([code listing](https://gist.github.com/mravn-google/4f7194e8c1f875eba189856eb40e6b1e), [diff](https://github.com/mravn/charts/commit/5a41b26279fb5ba334c219bf4f6d74cd33daf01b)). + +<DashImage figure src="images/1MuSAOLktwY8bTJdPGuoNqA.webp" alt="Arbitrary categories. Merge-based lerping." caption="Arbitrary categories. Merge-based lerping." /> + + +This works nicely, but is perhaps not the most efficient solution. We are repeatedly executing the merge algorithm in `BarChart.lerp`, once for every value of `t`. To fix that, we’ll implement the idea mentioned earlier to store reusable information in `BarChartTween`. + +```dart +class BarChartTween extends Tween<BarChart> { + BarChartTween(BarChart begin, BarChart end) : super(begin: begin, end: end) { + final bMax = begin.bars.length; + final eMax = end.bars.length; + var b = 0; + var e = 0; + while (b + e < bMax + eMax) { + if (b < bMax && (e == eMax || begin.bars[b] < end.bars[e])) { + _tweens.add(BarTween(begin.bars[b], begin.bars[b].collapsed)); + b++; + } else if (e < eMax && (b == bMax || end.bars[e] < begin.bars[b])) { + _tweens.add(BarTween(end.bars[e].collapsed, end.bars[e])); + e++; + } else { + _tweens.add(BarTween(begin.bars[b], end.bars[e])); + b++; + e++; + } + } + } + + final _tweens = <BarTween>[]; + + @override + BarChart lerp(double t) => BarChart( + List.generate( + _tweens.length, + (i) => _tweens[i].lerp(t), + ), + ); +} +``` + +We can now remove the static `BarChart.lerp` method ([diff](https://github.com/mravn/charts/commit/bb3b46f9384b8b90d50be1db59ce44ed83c61b2c)). + +Let’s summarize what we’ve learned about the tween concept so far: + +*Animate `T`s by tracing out a path in the space of all `T`s as the animation value runs from zero to one. Model the path with a `Tween<T>`.* + +*Generalize the `T` concept as needed until it encompasses all animation end points and intermediate values.* + +*Lerp between composite values by lerping corresponding components.* + +* *The correspondence should be based on semantics, not on accidental graphical co-location.* + +* *Where a component is missing in one animation end point, use an invisible component in its place, possibly derived from the other end point.* + +* *Where components form sorted lists, use the merge algorithm to bring semantically corresponding components on a par, introducing invisible components as needed to deal with one-sided merges.* + +*Consider implementing tweens using static `Xxx.lerp` methods to facilitate reuse in composite tween implementations. Where significant recomputation happens across calls to `Xxx.lerp` for a single animation path, consider moving the computation to the constructor of the `XxxTween` class, and let its instances host the computation outcome.* + +Armed with these insights, we are finally in position to animate more complex charts. We’ll do stacked bars, grouped bars, and stacked+grouped bars in quick succession: + +* Stacked bars are used for data sets where categories are two-dimensional and it makes sense to add up the numerical quantity represented by bar heights. An example might be revenue per product and geographical region. Stacking by product makes it easy to compare product performance in the global market. Stacking by region shows which regions are important. + +<DashImage figure src="images/1qKUFM56S-ZonH1amVDDXTw.webp" alt="Stacked bars." caption="Stacked bars." /> + + +* Grouped bars are also used for data sets with two-dimensional categories, but where it is not meaningful or desirable to stack the bars. For instance, if the numeric quantity is market share in percent per product and region, stacking by product makes no sense. Even where stacking does makes sense, grouping can be preferable as it makes it easier to do quantitative comparisons across both category dimensions at the same time. + +<DashImage figure src="images/1YiojxPiaWY7lB5v9iZVgDg.webp" alt="Grouped bars." caption="Grouped bars." /> + + +* Stacked+grouped bars support three-dimensional categories, like revenue per product, geographical region, and sales channel. + +<DashImage figure src="images/19ObVOKbos4DoQsmsqbMnRQ.webp" alt="Stacked+grouped bars." caption="Stacked+grouped bars." /> + + +In all three variants, animation can be used to visualize data set changes, thus introducing an additional dimension (typically time) without cluttering the charts. + +For the animation to be useful and not just pretty, we need to make sure that we lerp only between semantically corresponding components. So the bar segment used to represent the revenue for a particular product/region/channel in 2016 should be morphed into one representing revenue for the same product/region/channel in 2017 (if present). + +The merge algorithm can be used to ensure this. As you may have guessed from the preceding discussion, merge will be put to work at multiple levels, reflecting the dimensionality of the categories. We’ll merge stacks and bars in stacked charts, groups and bars in grouped charts, and all three in stacked+grouped charts. + +To accomplish that without a lot of code duplication, we’ll abstract the merge algorithm into a general utility, and put it in a file of its own, `tween.dart`: + +```dart +import 'package:flutter/animation.dart'; +import 'package:flutter/material.dart'; + +abstract class MergeTweenable<T> { + T get empty; + + Tween<T> tweenTo(T other); + + bool operator <(T other); +} + +class MergeTween<T extends MergeTweenable<T>> extends Tween<List<T>> { + MergeTween(List<T> begin, List<T> end) : super(begin: begin, end: end) { + final bMax = begin.length; + final eMax = end.length; + var b = 0; + var e = 0; + while (b + e < bMax + eMax) { + if (b < bMax && (e == eMax || begin[b] < end[e])) { + _tweens.add(begin[b].tweenTo(begin[b].empty)); + b++; + } else if (e < eMax && (b == bMax || end[e] < begin[b])) { + _tweens.add(end[e].empty.tweenTo(end[e])); + e++; + } else { + _tweens.add(begin[b].tweenTo(end[e])); + b++; + e++; + } + } + } + + final _tweens = <Tween<T>>[]; + + @override + List<T> lerp(double t) => List.generate( + _tweens.length, + (i) => _tweens[i].lerp(t), + ); +} + +``` + +The `MergeTweenable<T>` interface captures precisely what is needed to be able to create a tween of two sorted lists of `T`s by merging. We’ll instantiate the type parameter `T` with `Bar`, `BarStack`, and `BarGroup`, and make all these types implement `MergeTweenable<T>` ([diff](https://github.com/mravn/charts/commit/e7ec4c94bf560e483a267e60ee2b11c68932d4e0)). + +The [stacked](https://gist.github.com/mravn-google/78326296c59f0544d280a987d9ba39e2) ([diff](https://github.com/mravn/charts/commit/912b5eafd5296a549c6fbb6090bbcd3cb4bb4342)), [grouped](https://gist.github.com/mravn-google/d3f0f2a93cb478ab3a50dab03437a5d5) ([diff](https://github.com/mravn/charts/commit/b0b3af8115f3632971b33a4b74204dd8943db53e)), and [stacked+grouped](https://gist.github.com/mravn-google/cbd4a89e7b9e5431898a16727f7642b6) ([diff](https://github.com/mravn/charts/commit/44b0e5d07633edcf7770f5719ec1d1aa082a853c)) implementations have been written to be directly comparable. I encourage you to play around with the code: + +* Change the number of groups, stacks, and bars created by `BarChart.random`. + +* Change the color palettes. For stacked+grouped bars I’ve used a monochrome palette, because I think that looks nicer. You and your UX designer may disagree. + +* Replace `BarChart.random` and the floating action button with a year selector and create `BarChart` instances from realistic data sets. + +* Implement horizontal bar charts. + +* Implement other chart types (pie, line, stacked area). Animate them using `MergeTweenable<T>` or similar. + +* Add chart legends and/or labels and axes, then animate those too. + +The tasks of the last two bullets are quite challenging. Have fun. \ No newline at end of file diff --git a/sites/www/content/blog/zero-to-one-with-flutter/images/12t8GffL0BcNoGLU-IgHT9w.jpeg b/sites/www/content/blog/zero-to-one-with-flutter/images/12t8GffL0BcNoGLU-IgHT9w.jpeg new file mode 100644 index 0000000000..2655e6ce3b Binary files /dev/null and b/sites/www/content/blog/zero-to-one-with-flutter/images/12t8GffL0BcNoGLU-IgHT9w.jpeg differ diff --git a/sites/www/content/blog/zero-to-one-with-flutter/images/13KpUQjhZLrvwvjF0daKg9g.jpeg b/sites/www/content/blog/zero-to-one-with-flutter/images/13KpUQjhZLrvwvjF0daKg9g.jpeg new file mode 100644 index 0000000000..ed29ed1526 Binary files /dev/null and b/sites/www/content/blog/zero-to-one-with-flutter/images/13KpUQjhZLrvwvjF0daKg9g.jpeg differ diff --git a/sites/www/content/blog/zero-to-one-with-flutter/images/1OKV3RzTg89W3VxXnpAH3Eg.webp b/sites/www/content/blog/zero-to-one-with-flutter/images/1OKV3RzTg89W3VxXnpAH3Eg.webp new file mode 100644 index 0000000000..e1bc3b2954 Binary files /dev/null and b/sites/www/content/blog/zero-to-one-with-flutter/images/1OKV3RzTg89W3VxXnpAH3Eg.webp differ diff --git a/sites/www/content/blog/zero-to-one-with-flutter/images/1n76TpChNv8Q25WrfBiuWpw.webp b/sites/www/content/blog/zero-to-one-with-flutter/images/1n76TpChNv8Q25WrfBiuWpw.webp new file mode 100644 index 0000000000..955deb197c Binary files /dev/null and b/sites/www/content/blog/zero-to-one-with-flutter/images/1n76TpChNv8Q25WrfBiuWpw.webp differ diff --git a/sites/www/content/blog/zero-to-one-with-flutter/images/1soCdZ19Qugtv1YJewMQZGg.webp b/sites/www/content/blog/zero-to-one-with-flutter/images/1soCdZ19Qugtv1YJewMQZGg.webp new file mode 100644 index 0000000000..f23c28ff7d Binary files /dev/null and b/sites/www/content/blog/zero-to-one-with-flutter/images/1soCdZ19Qugtv1YJewMQZGg.webp differ diff --git a/sites/www/content/blog/zero-to-one-with-flutter/index.md b/sites/www/content/blog/zero-to-one-with-flutter/index.md new file mode 100644 index 0000000000..a1bfaf3d8b --- /dev/null +++ b/sites/www/content/blog/zero-to-one-with-flutter/index.md @@ -0,0 +1,611 @@ +--- +title: "Zero to One with Flutter" +description: "It was late summer 2016, and my first task as a new hire at the Google office in Aarhus, Denmark was to implement animated charts in an…" +publishDate: 2017-02-22 +author: mravn +image: images/1n76TpChNv8Q25WrfBiuWpw.webp +category: spotlight +layout: blog +--- + +*It was late summer 2016, and my first task as a new hire at the Google office in Aarhus, Denmark was to implement animated charts in an Android/iOS app using [Flutter](https://flutter.io) and [Dart](https://www.dartlang.org). Besides being a “Noogler”, I was new to Flutter, new to Dart, and new to animations. In fact, I had never done a mobile app before. My very first smartphone was just a few months old — bought in a fit of panic that I might fail the phone interview by answering the call with my old Nokia...* + +*I did have some prior experience with charts from desktop Java, but that wasn’t animated. I felt… weird. Partly a dinosaur, partly reborn.* + +<DashImage figure src="images/12t8GffL0BcNoGLU-IgHT9w.jpeg" /> + + +**TL;DR** Discovering the strength of Flutter’s widget and tween concepts by writing chart animations in Dart for an Android/iOS app. + +Updated on August 7, 2018 to use Dart 2 syntax. [GitHub repo](https://github.com/mravn/charts) added on October 17, 2018. Each step described below is a separate commit. + +Moving to a new development stack makes you aware of your priorities. Near the top of my list are these three: + +* **Strong concepts** deal effectively with complexity by providing simple, relevant ways of structuring thoughts, logic, or data. + +* **Clear code** lets us express those concepts cleanly, without being distracted by language pitfalls, excessive boilerplate, or auxiliary detail. + +* **Fast iteration** is key to experimentation and learning — and software development teams learn for a living: what the requirements really are, and how best to fulfill them with concepts expressed in code. + +Flutter is a new platform for developing Android and iOS apps from a single codebase, written in Dart. Since our requirements spoke of a fairly complex UI including animated charts, the idea of building it only once seemed very attractive. My tasks involved exercising Flutter’s CLI tools, some pre-built widgets, and its 2D rendering engine — in addition to writing a lot of plain Dart code to model and animate charts. I’ll share below some conceptual highlights of my learning experience, and provide a starting point for your own evaluation of the Flutter/Dart stack. + +<DashImage figure src="images/1OKV3RzTg89W3VxXnpAH3Eg.webp" alt="A simple animated bar chart, captured from an iOS simulator during development" caption="A simple animated bar chart, captured from an iOS simulator during development" /> + + +This is part one of a [two-part](https://medium.com/dartlang/zero-to-one-with-flutter-part-two-5aa2f06655cb) introduction to Flutter and its ‘widget’ and ‘tween’ concepts. I’ll illustrate the strength of these concepts by using them to display and animate charts like the one shown above. Full code samples should provide an impression of the level of code clarity achievable with Dart. And I’ll include enough detail that you should be able to follow along on your own laptop (and emulator or device), and experience the length of the Flutter development cycle. + +The starting point is a fresh [installation of Flutter](https://flutter.io/setup). Run + +``` +$ flutter doctor +``` + + +to check the setup: + +``` +$ flutter doctor +Doctor summary (to see all details, run flutter doctor -v): +[✓] Flutter + (Channel beta, v0.5.1, on Mac OS X 10.13.6 17G65, locale en-US) +[✓] Android toolchain - develop for Android devices + (Android SDK 28.0.0) +[✓] iOS toolchain - develop for iOS devices (Xcode 9.4) +[✓] Android Studio (version 3.1) +[✓] IntelliJ IDEA Community Edition (version 2018.2.1) +[✓] Connected devices (1 available) + +• No issues found! +``` + + +With enough check marks, you can create a Flutter app. Let’s call it `charts`: + +``` +$ flutter create charts +``` + + +That should give you a directory of the same name: + +``` +charts + android + ios + lib + main.dart +``` + + +About sixty files have been generated, making up a complete sample app that can be installed on both Android and iOS. We’ll do all our coding in `main.dart` and sibling files, with no pressing need to touch any of the other files or directories. + +You should verify that you can launch the sample app. Start an emulator or plug in a device, then execute + +``` +$ flutter run +``` + + +in the `charts` directory. You should then see a simple counting app on your emulator or device. It uses Material Design widgets, which is nice, but optional. As the top-most layer of the Flutter architecture, those widgets are completely replaceable. + +Let’s start by replacing the contents of `main.dart` with the code below, a simple starting point for playing with chart animations. + +```dart +import 'dart:math'; + +import 'package:flutter/material.dart'; + +void main() { + runApp(MaterialApp(home: ChartPage())); +} + +class ChartPage extends StatefulWidget { + @override + ChartPageState createState() => ChartPageState(); +} + +class ChartPageState extends State<ChartPage> { + final random = Random(); + int dataSet; + + void changeData() { + setState(() { + dataSet = random.nextInt(100); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Text('Data set: $dataSet'), + ), + floatingActionButton: FloatingActionButton( + child: Icon(Icons.refresh), + onPressed: changeData, + ), + ); + } +} + +``` + +Save the changes, then restart the app. You can do that from the terminal, by pressing `R`. This ‘full restart’ operation throws away the application state, then rebuilds the UI. For situations where the existing application state still makes sense after the code change, one can press `r` to do a ‘hot reload’, which only rebuilds the UI. There is also a Flutter plugin for IntelliJ IDEA providing the same functionality integrated with a Dart editor: + +<DashImage figure src="images/1soCdZ19Qugtv1YJewMQZGg.webp" alt="Screen shot from IntelliJ IDEA with an older version of the Flutter plug-in, showing the reload and restart buttons in the top-right corner. These buttons are enabled, if the app has been started from within the IDE. Newer versions of the plugin do hot reload on save." caption="Screen shot from IntelliJ IDEA with an older version of the Flutter plug-in, showing the reload and restart buttons in the top-right corner. These buttons are enabled, if the app has been started from within the IDE. Newer versions of the plugin do hot reload on save." /> + + +Once restarted, the app shows a centered text label saying `Data set: null` and a floating action button to refresh the data. Yes, humble beginnings. + +To get a feel for the difference between hot reload and full restart, try the following: After you’ve pressed the floating action button a few times, make a note of the current data set number, then replace `Icons.refresh` with `Icons.add` in the code, save, and do a hot reload. Observe that the button changes, but that the application state is retained; we’re still at the same place in the random stream of numbers. Now undo the icon change, save, and do a full restart. The application state has been reset, and we’re back to `Data set: null`. + +Our simple app shows two central aspects of the Flutter widget concept in action: + +* The user interface is defined by a tree of **immutable widgets** which is built via a foxtrot of constructor calls (where you get to configure widgets) and `build` methods (where widget implementations get to decide how their sub-trees look). The resulting tree structure for our app is shown below, with the main role of each widget in parentheses. As you can see, while the widget concept is quite broad, each concrete widget type typically has a very focused responsibility. + +``` +MaterialApp (navigation) + ChartPage (state management) + Scaffold (layout) + Center (layout) + Text (text) + FloatingActionButton (user interaction) + Icon (graphics) +``` + + +* With an immutable tree of immutable widgets defining the user interface, the only way to change that interface is to rebuild the tree. Flutter takes care of that, when the next frame is due. All we have to do is tell Flutter that some state on which a subtree depends has changed. The root of such a **state-dependent subtree** must be a `StatefulWidget`. Like any decent widget, a `StatefulWidget` is not mutable, but its subtree is built by a `State` object which is. Flutter retains `State` objects across tree rebuilds and attaches each to their respective widget in the new tree during building. They then determine how that widget’s subtree is built. In our app, `ChartPage` is a `StatefulWidget` with `ChartPageState` as its `State`. Whenever the user presses the button, we execute some code to change `ChartPageState.` We’ve demarcated the change with `setState` so that Flutter can do its housekeeping and schedule the widget tree for rebuilding. When that happens, `ChartPageState` will build a slightly different subtree rooted at the new instance of `ChartPage`. + +Immutable widgets and state-dependent subtrees are the main tools that Flutter puts at our disposal to address the complexities of state management in elaborate UIs responding to asynchronous events such as button presses, timer ticks, or incoming data. From my desktop experience I’d say this complexity is *very* real. Assessing the strength of Flutter’s approach is — and should be — an exercise for the reader: try it out on something non-trivial. + +Our charts app will stay simple in terms of widget structure, but we’ll do a bit of animated custom graphics. First step is to replace the textual representation of each data set with a very simple chart. Since a data set currently involves only a single number in the interval `0..100`, the chart will be a bar chart with a single bar, whose height is determined by that number. We’ll use an initial value of `50` to avoid a `null` height: + +```dart +import 'dart:math'; + +import 'package:flutter/material.dart'; + +void main() { + runApp(MaterialApp(home: ChartPage())); +} + +class ChartPage extends StatefulWidget { + @override + ChartPageState createState() => ChartPageState(); +} + +class ChartPageState extends State<ChartPage> { + final random = Random(); + int dataSet = 50; + + void changeData() { + setState(() { + dataSet = random.nextInt(100); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: CustomPaint( + size: Size(200.0, 100.0), + painter: BarChartPainter(dataSet.toDouble()), + ), + ), + floatingActionButton: FloatingActionButton( + child: Icon(Icons.refresh), + onPressed: changeData, + ), + ); + } +} + +class BarChartPainter extends CustomPainter { + static const barWidth = 10.0; + + BarChartPainter(this.barHeight); + + final double barHeight; + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = Colors.blue[400] + ..style = PaintingStyle.fill; + canvas.drawRect( + Rect.fromLTWH( + (size.width - barWidth) / 2.0, + size.height - barHeight, + barWidth, + barHeight, + ), + paint, + ); + } + + @override + bool shouldRepaint(BarChartPainter old) => barHeight != old.barHeight; +} + +``` + +`CustomPaint` is a widget that delegates painting to a `CustomPainter` strategy. Our implementation of that strategy draws a single bar. + +Next step is to add animation. Whenever the data set changes, we want the bar to change height smoothly rather than abruptly. Flutter has an `AnimationController` concept for orchestrating animations, and by registering a listener, we’re told when the animation value — a double running from zero to one — changes. Whenever that happens, we can call `setState` as before and update `ChartPageState`. + +For reasons of exposition, our first go at this will be ugly: + +```dart +import 'dart:math'; +import 'dart:ui' show lerpDouble; + +import 'package:flutter/animation.dart'; +import 'package:flutter/material.dart'; + +void main() { + runApp(MaterialApp(home: ChartPage())); +} + +class ChartPage extends StatefulWidget { + @override + ChartPageState createState() => ChartPageState(); +} + +class ChartPageState extends State<ChartPage> with TickerProviderStateMixin { + final random = Random(); + int dataSet = 50; + AnimationController animation; + double startHeight; // Strike one. + double currentHeight; // Strike two. + double endHeight; // Strike three. Refactor. + + @override + void initState() { + super.initState(); + animation = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + )..addListener(() { + setState(() { + currentHeight = lerpDouble( // Strike one. + startHeight, + endHeight, + animation.value, + ); + }); + }); + startHeight = 0.0; // Strike two. + currentHeight = 0.0; + endHeight = dataSet.toDouble(); + animation.forward(); + } + + @override + void dispose() { + animation.dispose(); + super.dispose(); + } + + void changeData() { + setState(() { + startHeight = currentHeight; // Strike three. Refactor. + dataSet = random.nextInt(100); + endHeight = dataSet.toDouble(); + animation.forward(from: 0.0); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: CustomPaint( + size: Size(200.0, 100.0), + painter: BarChartPainter(currentHeight), + ), + ), + floatingActionButton: FloatingActionButton( + child: Icon(Icons.refresh), + onPressed: changeData, + ), + ); + } +} + +class BarChartPainter extends CustomPainter { + static const barWidth = 10.0; + + BarChartPainter(this.barHeight); + + final double barHeight; + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = Colors.blue[400] + ..style = PaintingStyle.fill; + canvas.drawRect( + Rect.fromLTWH( + (size.width - barWidth) / 2.0, + size.height - barHeight, + barWidth, + barHeight, + ), + paint, + ); + } + + @override + bool shouldRepaint(BarChartPainter old) => barHeight != old.barHeight; +} + +``` + +Ouch. Complexity already rears its ugly head, and our data set is still just a single number! The code needed to set up animation control is a minor concern, as it doesn’t ramify when we get more chart data. The real problem is the variables `startHeight`, `currentHeight`, and `endHeight` which reflect the changes made to the data set and the animation value, and are updated in three different places. + +We are in need of a concept to deal with this mess. + +Enter **tweens**. While far from unique to Flutter, they are a delightfully simple concept for structuring animation code. Their main contribution is to replace the imperative approach above with a functional one. A tween is a *value*. It describes the path taken between two points in a space of other values, like bar charts, as the animation value runs from zero to one. + +<DashImage figure src="images/13KpUQjhZLrvwvjF0daKg9g.jpeg" /> + + +Tweens are generic in the type of these other values, and can be expressed in Dart as objects of the type `Tween<T>`: + +```dart +abstract class Tween<T> { + final T begin; + final T end; + + Tween(this.begin, this.end); + + T lerp(double t); +} + +``` + +The jargon `lerp` comes from the field of computer graphics and is short for both *linear interpolation* (as a noun) and *linearly interpolate* (as a verb). The parameter `t` is the animation value, and a tween should thus lerp from `begin` (when `t` is zero) to `end` (when `t` is one). + +The Flutter SDK’s [`Tween<T>`](https://docs.flutter.io/flutter/animation/Tween-class.html) class is very similar to the above, but is a concrete class that supports mutating `begin` and `end`. I’m not entirely sure why that choice was made, but there are probably good reasons for it in areas of the SDK’s animation support that I have yet to explore. In the following, I’ll use the Flutter `Tween<T>`, but pretend it is immutable. + +We can clean up our code using a single `Tween<double>` for the bar height: + +```dart +import 'dart:math'; + +import 'package:flutter/animation.dart'; +import 'package:flutter/material.dart'; + +void main() { + runApp(MaterialApp(home: ChartPage())); +} + +class ChartPage extends StatefulWidget { + @override + ChartPageState createState() => ChartPageState(); +} + +class ChartPageState extends State<ChartPage> with TickerProviderStateMixin { + final random = Random(); + int dataSet = 50; + AnimationController animation; + Tween<double> tween; + + @override + void initState() { + super.initState(); + animation = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + tween = Tween<double>(begin: 0.0, end: dataSet.toDouble()); + animation.forward(); + } + + @override + void dispose() { + animation.dispose(); + super.dispose(); + } + + void changeData() { + setState(() { + dataSet = random.nextInt(100); + tween = Tween<double>( + begin: tween.evaluate(animation), + end: dataSet.toDouble(), + ); + animation.forward(from: 0.0); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: CustomPaint( + size: Size(200.0, 100.0), + painter: BarChartPainter(tween.animate(animation)), + ), + ), + floatingActionButton: FloatingActionButton( + child: Icon(Icons.refresh), + onPressed: changeData, + ), + ); + } +} + +class BarChartPainter extends CustomPainter { + static const barWidth = 10.0; + + BarChartPainter(Animation<double> animation) + : animation = animation, + super(repaint: animation); + + final Animation<double> animation; + + @override + void paint(Canvas canvas, Size size) { + final barHeight = animation.value; + final paint = Paint() + ..color = Colors.blue[400] + ..style = PaintingStyle.fill; + canvas.drawRect( + Rect.fromLTWH( + (size.width - barWidth) / 2.0, + size.height - barHeight, + barWidth, + barHeight, + ), + paint, + ); + } + + @override + bool shouldRepaint(BarChartPainter old) => false; +} + +``` + +We’re using `Tween` for packaging the bar height animation end-points in a single value. It interfaces neatly with the `AnimationController` and `CustomPainter`, avoiding widget tree rebuilds during animation as the Flutter infrastructure now marks `CustomPaint` for repaint at each animation tick, rather than marking the whole `ChartPage` subtree for rebuild, relayout, and repaint. These are definite improvements. But there’s more to the tween concept; it offers *structure* to organize our thoughts and code, and we haven’t really taken that seriously. The tween concept says, + +*Animate `T`s by tracing out a path in the space of all `T`s as the animation value runs from zero to one. Model the path with a `Tween<T>`.* + +In the code above, `T` is a `double`, but we do not want to animate `double`s, we want to animate bar charts! Well, OK, single bars for now, but the concept is strong, and it scales, if we let it. + +(You may be wondering why we don’t take that argument a step further and insist on animating data sets rather than their representations as bar charts. That’s because data sets — in contrast to bar charts which are graphical objects — generally do not inhabit spaces where smooth paths exist. Data sets for bar charts typically involve numerical data mapped against discrete data categories. But without the spatial representation as bar charts, there is no reasonable notion of a smooth path between two data sets involving different categories.) + +Returning to our code, we’ll need a `Bar` type and a `BarTween` to animate it. Let’s extract the bar-related classes into their own `bar.dart` file next to `main.dart`: + +```dart +import 'dart:ui' show lerpDouble; + +import 'package:flutter/animation.dart'; +import 'package:flutter/material.dart'; + +class Bar { + Bar(this.height); + + final double height; + + static Bar lerp(Bar begin, Bar end, double t) { + return Bar(lerpDouble(begin.height, end.height, t)); + } +} + +class BarTween extends Tween<Bar> { + BarTween(Bar begin, Bar end) : super(begin: begin, end: end); + + @override + Bar lerp(double t) => Bar.lerp(begin, end, t); +} + +class BarChartPainter extends CustomPainter { + static const barWidth = 10.0; + + BarChartPainter(Animation<Bar> animation) + : animation = animation, + super(repaint: animation); + + final Animation<Bar> animation; + + @override + void paint(Canvas canvas, Size size) { + final bar = animation.value; + final paint = Paint() + ..color = Colors.blue[400] + ..style = PaintingStyle.fill; + canvas.drawRect( + Rect.fromLTWH( + (size.width - barWidth) / 2.0, + size.height - bar.height, + barWidth, + bar.height, + ), + paint, + ); + } + + @override + bool shouldRepaint(BarChartPainter old) => false; +} + +``` + +I’m following a Flutter SDK convention here in defining `BarTween.lerp` in terms of a static method on the `Bar` class. This works well for simple types like `Bar`, `Color`, `Rect` and many others, but we’ll need to reconsider the approach for more involved chart types. There is no `double.lerp` in the Dart SDK, so we’re using the function `lerpDouble` from the `dart:ui` package to the same effect. + +Our app can now be re-expressed in terms of bars as shown in the code below; I’ve taken the opportunity to dispense of the `dataSet` field. + +```dart +import 'dart:math'; + +import 'package:flutter/animation.dart'; +import 'package:flutter/material.dart'; + +import 'bar.dart'; + +void main() { + runApp(MaterialApp(home: ChartPage())); +} + +class ChartPage extends StatefulWidget { + @override + ChartPageState createState() => ChartPageState(); +} + +class ChartPageState extends State<ChartPage> with TickerProviderStateMixin { + final random = Random(); + AnimationController animation; + BarTween tween; + + @override + void initState() { + super.initState(); + animation = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + tween = BarTween(Bar(0.0), Bar(50.0)); + animation.forward(); + } + + @override + void dispose() { + animation.dispose(); + super.dispose(); + } + + void changeData() { + setState(() { + tween = BarTween( + tween.evaluate(animation), + Bar(random.nextDouble() * 100.0), + ); + animation.forward(from: 0.0); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: CustomPaint( + size: Size(200.0, 100.0), + painter: BarChartPainter(tween.animate(animation)), + ), + ), + floatingActionButton: FloatingActionButton( + child: Icon(Icons.refresh), + onPressed: changeData, + ), + ); + } +} + +``` + +The new version is longer, and the extra code should carry its weight. It will, as we tackle increased chart complexity in [part two](https://medium.com/@mravn/zero-to-one-with-flutter-part-two-5aa2f06655cb). Our requirements speak of colored bars, multiple bars, partial data, stacked bars, grouped bars, stacked and grouped bars, … all of it animated. Stay tuned. + +<DashImage figure src="images/1n76TpChNv8Q25WrfBiuWpw.webp" alt="A preview of one of the animations we’ll do in part two." caption="A preview of one of the animations we’ll do in part two." /> diff --git a/sites/www/firebase.json b/sites/www/firebase.json index bdac6d3ec9..f033ce7883 100644 --- a/sites/www/firebase.json +++ b/sites/www/firebase.json @@ -25,8 +25,6 @@ { "source": "/adoptawidget", "destination": "https://docs.flutter.dev/adoptawidget", "type": 301 }, { "source": "/ads", "destination": "/monetization", "type": 301 }, - { "source": "/blog", "destination": "https://blog.flutter.dev/", "type": 302 }, - { "source": "/blog/:rest*", "destination": "https://blog.flutter.dev/:rest*", "type": 302 }, { "source": "/clock", "destination": "https://docs.flutter.dev/clock", "type": 302 }, { "source": "/community/china", "destination": "https://docs.flutter.dev/community/china", "type": 301 }, { "source": "/desktop", "destination": "/development/desktop", "type": 301 }, diff --git a/sites/www/lib/main.client.options.dart b/sites/www/lib/main.client.options.dart index 48893019e6..a2cac8325f 100644 --- a/sites/www/lib/main.client.options.dart +++ b/sites/www/lib/main.client.options.dart @@ -26,6 +26,16 @@ import 'package:flutter_website/src/components/pages/showcase_grid.dart' deferred as _showcase_grid; import 'package:flutter_website/src/models/content/banner_content.dart' as _banner_content; +import 'package:site_shared/components/blog/client/blog_categories.dart' + deferred as _blog_categories; +import 'package:site_shared/components/blog/client/share_button.dart' + deferred as _share_button; +import 'package:site_shared/components/common/client/collapse_button.dart' + deferred as _collapse_button; +import 'package:site_shared/components/common/client/copy_button.dart' + deferred as _copy_button; +import 'package:site_shared/components/dartpad/dartpad_injector.dart' + deferred as _dartpad_injector; import 'package:site_shared/components/utils/component_ref.dart' as _component_ref; @@ -135,5 +145,49 @@ ClientOptions get defaultClientOptions => ClientOptions( ), loader: _showcase_grid.loadLibrary, ), + 'site_shared:blog_categories': ClientLoader( + (p) => _blog_categories.BlogCategories( + categories: (p['categories'] as List<Object?>) + .map( + (i) => _blog_categories.BlogCategory.fromMap( + i as Map<String, Object?>, + ), + ) + .toList(), + ), + loader: _blog_categories.loadLibrary, + ), + 'site_shared:share_button': ClientLoader( + (p) => _share_button.ShareButton( + url: p['url'] as String, + title: p['title'] as String, + ), + loader: _share_button.loadLibrary, + ), + 'site_shared:collapse_button': ClientLoader( + (p) => _collapse_button.CollapseButton( + classes: (p['classes'] as List<Object?>).cast<String>(), + title: p['title'] as String?, + ), + loader: _collapse_button.loadLibrary, + ), + 'site_shared:copy_button': ClientLoader( + (p) => _copy_button.CopyButton( + buttonText: p['buttonText'] as String?, + toCopy: p['toCopy'] as String?, + classes: (p['classes'] as List<Object?>).cast<String>(), + title: p['title'] as String?, + ), + loader: _copy_button.loadLibrary, + ), + 'site_shared:dartpad_injector': ClientLoader( + (p) => _dartpad_injector.DartPadInjector( + title: p['title'] as String, + theme: p['theme'] as String?, + height: p['height'] as String?, + runAutomatically: p['runAutomatically'] as bool, + ), + loader: _dartpad_injector.loadLibrary, + ), }, ); diff --git a/sites/www/lib/main.server.dart b/sites/www/lib/main.server.dart index e8772f2c18..3ab1f6bb96 100644 --- a/sites/www/lib/main.server.dart +++ b/sites/www/lib/main.server.dart @@ -3,12 +3,18 @@ // can be found in the LICENSE file. import 'package:jaspr/server.dart'; -import 'package:jaspr_content/jaspr_content.dart'; +import 'package:jaspr_content/components/file_tree.dart'; +import 'package:jaspr_content/jaspr_content.dart' hide BlogLayout; import 'package:jaspr_content/theme.dart'; +import 'package:site_shared/components/blog/blog_index.dart'; +import 'package:site_shared/components/common/youtube_embed.dart'; import 'package:site_shared/components/utils/define_component.dart'; +import 'package:site_shared/page_extensions.dart'; import 'main.server.options.dart'; +import 'src/components/common/dash_image.dart'; import 'src/components/common/image.dart'; +import 'src/layouts/blog_layout.dart'; import 'src/layouts/consultants_tos_layout.dart'; import 'src/layouts/default_layout.dart'; import 'src/layouts/showcase_story_layout.dart'; @@ -41,6 +47,7 @@ void main() async { final assetManager = AssetManager( directory: 'content', outputPrefix: 'assets', + dataProperties: const {'page.image'}, assetTransformers: [ TrackingAssetTransformer(), ResizingAssetTransformer(), @@ -76,10 +83,15 @@ void main() async { configResolver: PageConfig.all( dataLoaders: [ FilesystemDataLoader('content'), + assetManager.dataLoader, ], parsers: [const MarkdownParser()], - extensions: [ShowcaseStoryExtension(), assetManager.pageExtension], + extensions: [ + ShowcaseStoryExtension(), + const CodeBlockProcessor(defaultTitle: 'Runnable Flutter example'), + assetManager.pageExtension, + ], components: [ defineComponent('HomePage', const HomePage()), defineComponent('DevelopmentPage', const DevelopmentPage()), @@ -105,11 +117,20 @@ void main() async { defineComponent('FlipPage', const FlipPage()), defineComponent('NewsPage', const NewsPage()), defineComponentWithAttrs('Image', Image.fromAttrs), + + CustomComponent( + pattern: RegExp('BlogIndex', caseSensitive: false), + builder: (_, _, _) => const BlogIndex(), + ), + const DashImage(), + const YoutubeEmbed(), + const FileTree(), ], layouts: [ DefaultLayout(), ConsultantsTosLayout(), ShowcaseStoryLayout(), + BlogLayout(), ], theme: const ContentTheme.none(), ), diff --git a/sites/www/lib/main.server.options.dart b/sites/www/lib/main.server.options.dart index 5440f946cd..3a88eb316a 100644 --- a/sites/www/lib/main.server.options.dart +++ b/sites/www/lib/main.server.options.dart @@ -21,6 +21,17 @@ import 'package:flutter_website/src/components/pages/games_adaptive_media.dart' as _games_adaptive_media; import 'package:flutter_website/src/components/pages/showcase_grid.dart' as _showcase_grid; +import 'package:jaspr_content/components/file_tree.dart' as _file_tree; +import 'package:site_shared/components/blog/client/blog_categories.dart' + as _blog_categories; +import 'package:site_shared/components/blog/client/share_button.dart' + as _share_button; +import 'package:site_shared/components/common/client/collapse_button.dart' + as _collapse_button; +import 'package:site_shared/components/common/client/copy_button.dart' + as _copy_button; +import 'package:site_shared/components/dartpad/dartpad_injector.dart' + as _dartpad_injector; /// Default [ServerOptions] for use with your Jaspr project. /// @@ -74,8 +85,31 @@ ServerOptions get defaultServerOptions => ServerOptions( 'showcase_grid', params: __showcase_gridShowcaseGrid, ), + _blog_categories.BlogCategories: + ClientTarget<_blog_categories.BlogCategories>( + 'site_shared:blog_categories', + params: __blog_categoriesBlogCategories, + ), + _share_button.ShareButton: ClientTarget<_share_button.ShareButton>( + 'site_shared:share_button', + params: __share_buttonShareButton, + ), + _collapse_button.CollapseButton: + ClientTarget<_collapse_button.CollapseButton>( + 'site_shared:collapse_button', + params: __collapse_buttonCollapseButton, + ), + _copy_button.CopyButton: ClientTarget<_copy_button.CopyButton>( + 'site_shared:copy_button', + params: __copy_buttonCopyButton, + ), + _dartpad_injector.DartPadInjector: + ClientTarget<_dartpad_injector.DartPadInjector>( + 'site_shared:dartpad_injector', + params: __dartpad_injectorDartPadInjector, + ), }, - styles: () => [], + styles: () => [..._file_tree.FileTree.styles], ); Map<String, Object?> __carouselCarousel(_carousel.Carousel c) => { @@ -108,3 +142,27 @@ Map<String, Object?> __games_adaptive_mediaGamesAdaptiveMedia( Map<String, Object?> __showcase_gridShowcaseGrid( _showcase_grid.ShowcaseGrid c, ) => {'data': c.data, 'items': c.items.map((i) => i.toId()).toList()}; +Map<String, Object?> __blog_categoriesBlogCategories( + _blog_categories.BlogCategories c, +) => {'categories': c.categories.map((i) => i.toMap()).toList()}; +Map<String, Object?> __share_buttonShareButton(_share_button.ShareButton c) => { + 'url': c.url, + 'title': c.title, +}; +Map<String, Object?> __collapse_buttonCollapseButton( + _collapse_button.CollapseButton c, +) => {'classes': c.classes, 'title': c.title}; +Map<String, Object?> __copy_buttonCopyButton(_copy_button.CopyButton c) => { + 'buttonText': c.buttonText, + 'toCopy': c.toCopy, + 'classes': c.classes, + 'title': c.title, +}; +Map<String, Object?> __dartpad_injectorDartPadInjector( + _dartpad_injector.DartPadInjector c, +) => { + 'title': c.title, + 'theme': c.theme, + 'height': c.height, + 'runAutomatically': c.runAutomatically, +}; diff --git a/sites/www/lib/src/components/common/dash_image.dart b/sites/www/lib/src/components/common/dash_image.dart new file mode 100644 index 0000000000..b52c47e011 --- /dev/null +++ b/sites/www/lib/src/components/common/dash_image.dart @@ -0,0 +1,66 @@ +// Copyright 2025 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:jaspr/dom.dart'; +import 'package:jaspr/jaspr.dart'; +import 'package:jaspr_content/jaspr_content.dart'; +import 'package:site_shared/markdown.dart'; + +class DashImage with CustomComponentBase { + const DashImage(); + + @override + Pattern get pattern => RegExp('DashImage', caseSensitive: false); + + @override + Component apply(_, Map<String, String> attributes, _) { + final isFigure = attributes.containsKey('figure'); + final imgSrc = + attributes['src'] ?? + (throw Exception( + '<DashImage> component requires an "image" attribute.', + )); + + final caption = attributes['caption'] ?? ''; + final alt = attributes['alt'] ?? caption; + + final figureClass = isFigure ? attributes['class'] : null; + final imgClass = attributes[isFigure ? 'img-class' : 'class']; + + final style = + [ + ?attributes['img-style'], + if (attributes['width'] case final w?) 'width: $w', + if (attributes['height'] case final h?) 'height: $h', + ] + .map((style) => style.trim()) + .map((style) => style.endsWith(';') ? style : '$style;') + .join(' '); + + final child = Builder( + builder: (context) => Component.fragment([ + img( + src: context.resolveAsset(imgSrc), + alt: alt, + classes: imgClass, + attributes: { + if (style.isNotEmpty) 'style': style, + }, + ), + if (caption.isNotEmpty) + figcaption(classes: 'figure-caption', [ + DashMarkdown(content: caption, inline: true), + ]), + ]), + ); + + if (isFigure) { + return figure(classes: figureClass, [ + child, + ]); + } + + return child; + } +} diff --git a/sites/www/lib/src/components/layout/header.dart b/sites/www/lib/src/components/layout/header.dart index dc2af3aee0..687a8eb904 100644 --- a/sites/www/lib/src/components/layout/header.dart +++ b/sites/www/lib/src/components/layout/header.dart @@ -147,6 +147,8 @@ class _HeaderState extends State<Header> { 'site-header', ].join(' '); + final isBlog = component.pageUrl.startsWith('/blog'); + return header( classes: headerClasses, attributes: const {'role': 'banner'}, @@ -164,6 +166,13 @@ class _HeaderState extends State<Header> { img(src: component.defaultLogoSrc, alt: 'Flutter'), ]), ]), + if (isBlog) + const a( + href: '/blog', + classes: 'subtype', + attributes: {'translate': 'no'}, + [.text('Blog')], + ), ]), div(classes: 'tray', [ nav( diff --git a/sites/www/lib/src/data/nav_items.dart b/sites/www/lib/src/data/nav_items.dart index f32e4e3e44..82cb93df7d 100644 --- a/sites/www/lib/src/data/nav_items.dart +++ b/sites/www/lib/src/data/nav_items.dart @@ -38,6 +38,10 @@ final List<NavItem> headerNavItems = [ label: 'Showcase', href: '/showcase', ), + NavItem( + label: 'Blog', + href: '/blog', + ), NavItem( label: 'Docs', href: 'https://docs.flutter.dev', @@ -78,8 +82,4 @@ final List<NavItem> headerNavItems = [ ), ], ), - NavItem( - label: 'Blog', - href: 'https://blog.flutter.dev', - ), ]; diff --git a/sites/www/lib/src/layouts/blog_layout.dart b/sites/www/lib/src/layouts/blog_layout.dart new file mode 100644 index 0000000000..1b990089c5 --- /dev/null +++ b/sites/www/lib/src/layouts/blog_layout.dart @@ -0,0 +1,93 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:jaspr/dom.dart'; +import 'package:jaspr/jaspr.dart'; +import 'package:jaspr_content/jaspr_content.dart'; +import 'package:site_shared/blog.dart'; +import 'package:site_shared/components/blog/blog_next_posts.dart'; +import 'package:site_shared/components/blog/post_info.dart'; +import 'package:site_shared/components/common/breadcrumbs.dart'; +import 'package:site_shared/util.dart'; + +import 'default_layout.dart'; + +/// The Jaspr Content layout to use for blog pages, +/// adding elements such as breadcrumbs. +class BlogLayout extends DefaultLayout { + BlogLayout(); + + @override + String get name => 'blog'; + + @override + List<String> get defaultBodyTags => ['interior', 'blog']; + + @override + Component buildLayout(Page page, Component child) { + final pageData = page.data.page; + final pageTitle = pageData['title'] as String? ?? 'Untitled'; + + final isPost = page.url.startsWith('/blog/'); + if (isPost && page.data.page['titleBase'] == null) { + page.apply( + data: { + 'page': {'titleBase': 'The Dart Blog'}, + }, + ); + } + + final categorySlug = pageData['category'] as String?; + final categories = page.blogCategories; + final pageCategory = categories + .where((c) => c.slug == categorySlug) + .firstOrNull; + + return super.buildLayout( + page, + main_([ + article(classes: 'content', [ + div(classes: 'content ${isPost ? 'post-content' : ''}', [ + div(id: 'site-content-title', [ + if (isPost) + PageBreadcrumbs( + crumbs: [ + const BreadcrumbItem( + title: 'The Flutter Blog', + url: '/blog', + ), + if (pageCategory != null && + pageCategory.slug != 'other') ...[ + BreadcrumbItem( + title: pageCategory.label, + url: '/blog?category=${pageCategory.slug}', + ), + ], + BreadcrumbItem( + title: pageTitle.split(':').first, + url: page.url, + ), + ], + ), + h1(id: 'document-title', [ + if (pageData['underscore_breaker_titles'] == true) + ...splitByUnderscore(pageTitle) + else + .text(pageTitle), + ]), + if (pageData['description'] != null) + p(classes: 'blog-subtitle', [ + .text(pageData['description'] as String), + ]), + ]), + if (isPost) PostInfo(post: Post(pageData), url: page.url), + child, + if (isPost) + BlogNextPosts(currentPage: page, category: pageCategory), + ]), + ]), + ]), + ); + } +} diff --git a/sites/www/lib/src/layouts/default_layout.dart b/sites/www/lib/src/layouts/default_layout.dart index cd1f05e835..3e50730d6b 100644 --- a/sites/www/lib/src/layouts/default_layout.dart +++ b/sites/www/lib/src/layouts/default_layout.dart @@ -105,12 +105,28 @@ class DefaultLayout extends PageLayout { 'https://fonts.googleapis.com/css2?family=Google+Sans+Flex:opsz,wght@6..120,400..700&family=Google+Sans+Code:ital,wght@0,400..700;1,400..700&display=swap', rel: 'stylesheet', ), + const link( + rel: 'stylesheet', + href: + 'https://fonts.googleapis.com/css2?' + 'family=Roboto+Serif:ital,opsz,wght@' + '0,8..72,400..700;1,8..72,400..700' + '&display=swap', + ), const link( href: 'https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,400,0..1,0&display=block', rel: 'stylesheet', ), + const link( + rel: 'stylesheet', + href: + 'https://fonts.googleapis.com/css2?' + 'family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@' + '24,400,0..1,0', + ), + // Set up standard cookie notification bar. const link( href: diff --git a/sites/www/lib/styles/components/_button.scss b/sites/www/lib/styles/components/_btn.scss similarity index 96% rename from sites/www/lib/styles/components/_button.scss rename to sites/www/lib/styles/components/_btn.scss index 7e2bb6a3c3..08e751097f 100644 --- a/sites/www/lib/styles/components/_button.scss +++ b/sites/www/lib/styles/components/_btn.scss @@ -1,5 +1,4 @@ -.btn, -button { +.btn { align-items: center; background-color: transparent; border-radius: var(--ui-border-radius); @@ -45,7 +44,8 @@ button { background-color: transparent !important; border: none !important; color: black; - padding: 0 var(--gutter-sm);; + padding: 0 var(--gutter-sm); + ; &:hover { background-color: transparent !important; @@ -90,7 +90,7 @@ button { main { - .btn, button { + .btn { border: 1px solid var(--blue-6); font-size: var(--font-size-default); @@ -99,4 +99,4 @@ main { color: white; } } -} +} \ No newline at end of file diff --git a/sites/www/lib/styles/components/_content.scss b/sites/www/lib/styles/components/_content.scss new file mode 100644 index 0000000000..c552dc907a --- /dev/null +++ b/sites/www/lib/styles/components/_content.scss @@ -0,0 +1,229 @@ +article.content { + .table { + width: 100%; + border-spacing: 0; + + thead { + vertical-align: bottom; + background-color: var(--site-raised-bgColor); + + th { + border-top: 1px solid var(--site-outline-variant); + text-align: start; + } + } + + tbody { + tr { + vertical-align: top; + + &:nth-of-type(odd) { + background-color: rgb(var(--site-interaction-base-values) / 3%) + } + } + } + + td, + th { + border: none; + border-top: 1px solid var(--site-inset-borderColor); + padding: .75rem; + } + } + + // If table head has empty children, just hide it + // since markdown-it's tables don't support headless tables. + thead:has(th:empty) { + display: none; + } + + .table-wrapper { + overflow-x: auto; + margin-block-start: 1rem; + margin-block-end: 1rem; + } + + #site-content-title { + position: relative; + font-family: var(--site-ui-fontFamily); + margin-block-end: 1.5rem; + scroll-margin-top: calc(var(--site-header-height) + var(--site-subheader-height) + 1.25rem); + + &.wrap { + display: flex; + flex-direction: column; + gap: 0.35rem; + min-width: 0; + background-color: var(--site-raised-bgColor); + padding: 1rem; + border-radius: var(--site-radius); + } + + h1 { + text-wrap: pretty; + min-width: 0; + max-width: 100%; + word-break: normal; + overflow-wrap: break-word; + margin-top: 0; + + @media (max-width: 390px) { + font-size: 2.375rem; + } + + @media (max-width: 350px) { + font-size: 2rem; + } + } + + .page-description { + margin-block: 0; + } + + #page-header-options { + position: absolute; + top: 0.75rem; + right: 0.75rem; + + >button { + color: var(--site-base-fgColor-alt); + } + + .dropdown-content { + right: -0.5rem; + } + } + } + + h1, + h2, + h3, + h4, + h5, + h6 { + + // Push # link targets clear of page header. + &[id] { + scroll-margin-top: calc(var(--site-header-height) + var(--site-subheader-height) + 1.25rem); + } + + // Let the wrapper set the bottom margin. + margin-bottom: 0; + } + + // Push # link targets clear of page header. + a[id] { + scroll-margin-top: calc(var(--site-header-height) + var(--site-subheader-height) + 1.25rem); + } + + .header-wrapper { + display: flex; + margin-block-start: 1.5rem; + margin-block-end: 0.75rem; + align-items: center; + + >h1, + h2, + h3, + h4, + h5, + h6 { + margin: 0; + } + + .heading-link { + border-radius: 0.125rem; + margin-left: 0.4rem; + font-size: 1.3rem; + line-height: 1; + transition: all 0.1s ease-in-out; + overflow: hidden; + color: var(--site-base-fgColor-alt); + opacity: 0; + text-decoration: none; + + &:hover { + color: var(--site-link-fgColor); + } + + &:focus { + opacity: 1; + } + + &:active { + color: var(--site-link-fgColor-active); + } + } + + &:hover { + .heading-link { + opacity: 1 + } + } + } + + .footnote { + margin: 0; + padding: 0; + list-style-type: none; + + li { + counter-increment: step-counter; + margin-bottom: 10px; + position: relative; + + &:before { + content: counter(step-counter); + position: absolute; + left: -25px; + top: -3px; + font-size: 20px; + font-weight: var(--site-fontWeight-bold); + padding: 3px 8px; + } + } + } + + .content { + >p { + + >i.material-symbols, + >span.material-symbols { + vertical-align: bottom; + user-select: none; + } + } + + >lite-youtube { + margin-block-end: 1rem; + } + } + + lite-youtube:not(.full-width) { + max-width: 560px; + } + + figure { + margin: 0; + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + margin-block: 2rem; + + img { + max-width: 100%; + height: auto; + border-radius: var(--site-radius); + } + + } + + .figure-caption { + margin-top: 0.75rem; + font-size: 0.9rem; + color: var(--site-base-fgColor-alt); + text-align: center; + font-style: italic; + } +} \ No newline at end of file diff --git a/sites/www/lib/styles/components/_header.scss b/sites/www/lib/styles/components/_header.scss index 88f76c1b90..cedbb6151f 100644 --- a/sites/www/lib/styles/components/_header.scss +++ b/sites/www/lib/styles/components/_header.scss @@ -20,6 +20,7 @@ align-items: center; justify-content: center; background: linear-gradient(124.64deg, #027DFD 0%, #833EF2 100%); + border-radius: 0; color: white; font-size: var(--font-size-default); font-weight: var(--font-weight-bold); @@ -62,7 +63,7 @@ max-height: var(--ui-header-navigation-height); padding: 0 var(--gutter-sm); - > div { + >div { display: flex; align-items: center; } @@ -93,6 +94,7 @@ 0% { opacity: 0; } + 100% { opacity: 1; top: var(--dd-offset-top); @@ -103,6 +105,7 @@ 0% { opacity: 0; } + 100% { opacity: 1; left: 0; @@ -117,7 +120,7 @@ margin: 0; } - > ul { + >ul { display: flex; text-wrap: nowrap; list-style: none; @@ -125,7 +128,7 @@ font-weight: var(--font-weight-bold); font-size: var(--font-size-heading-4); - > li { + >li { position: relative; z-index: 2; display: block; @@ -190,7 +193,8 @@ display: none; flex-grow: 1; height: 100%; - margin: 0 -10px; /* close gap on icons */ + margin: 0 -10px; + /* close gap on icons */ input { padding: 0 8px; @@ -203,7 +207,8 @@ width: 100%; &::placeholder { - color: var(--grey-4); /* Lighter while the header overlays hero media. */ + color: var(--grey-4); + /* Lighter while the header overlays hero media. */ } } } @@ -281,7 +286,7 @@ animation-timing-function: var(--ui-anim-func); animation-fill-mode: forwards; - > ul { + >ul { position: relative; top: 70px; display: inline-block; @@ -290,11 +295,11 @@ width: 100%; overflow-y: auto; - > li { + >li { margin-bottom: 5px; &.selected { - > a .icon { + >a .icon { transform: rotate(180deg); } @@ -309,7 +314,7 @@ } } - > a { + >a { justify-content: left; padding-left: var(--gutter-sm); margin-bottom: 3px; @@ -344,7 +349,8 @@ a.btn { color: var(--grey-2); padding: 9px 16px; - display: inline-block; /* not full width bg */ + display: inline-block; + /* not full width bg */ font-weight: var(--font-weight-normal); height: auto; @@ -403,8 +409,8 @@ box-shadow: none; font-size: var(--font-size-default); - > ul { - > li { + >ul { + >li { display: inline-block; margin-left: var(--spacer-sm); @@ -414,6 +420,7 @@ } &:hover { + /* keep li highlighted while over dropdown */ a.btn { background-color: rgba(250, 250, 250, 0.15); @@ -422,6 +429,7 @@ .dd-connector { display: block; } + .dd { display: flex; animation-duration: 300ms; @@ -575,7 +583,8 @@ body.in-content { transition: background-color 300ms, box-shadow 200ms, top 300px ease; background-color: white; box-shadow: 0 1px 6px rgba(0, 0, 0, 0.2); - top: 0 !important; /* Override `.hide`. */ + top: 0 !important; + /* Override `.hide`. */ .logo { &.logo-contrast { @@ -591,10 +600,10 @@ body.in-content { transition: color 300ms; color: black; - > ul { - > li { + >ul { + >li { &:hover { - > a.btn { + >a.btn { background-color: transparent; } } @@ -649,4 +658,4 @@ body.in-content { color: white; } } -} +} \ No newline at end of file diff --git a/sites/www/lib/styles/core/_base.scss b/sites/www/lib/styles/core/_base.scss index 8c16e6cfc9..41cfbf1207 100644 --- a/sites/www/lib/styles/core/_base.scss +++ b/sites/www/lib/styles/core/_base.scss @@ -35,6 +35,15 @@ h6 { letter-spacing: -0.005em; font-weight: var(--font-weight-bold); color: black; +} + + +:is(h1, + h2, + h3, + h4, + h5, + h6):not(:where(.post-content *)) { margin-block-start: 0; margin-block-end: 0; margin-inline-start: 0; @@ -66,7 +75,7 @@ h4.eyebrow { font-size: var(--font-size-heading-4); } -p { +p:not(:where(.post-content *)) { line-height: 1.4; color: var(--grey-6); @@ -74,3 +83,32 @@ p { color: var(--blue-6); } } + +.material-symbols { + font-family: var(--site-icon-fontFamily); + font-variation-settings: + 'FILL' 0, + 'wght' 400, + 'GRAD' 0, + 'opsz' 24; + + font-weight: normal; + font-style: normal; + font-size: 24px; + line-height: 1; + letter-spacing: normal; + text-transform: none; + display: inline-block; + vertical-align: middle; + white-space: nowrap; + word-wrap: normal; + direction: ltr; + font-feature-settings: 'liga'; + -webkit-font-feature-settings: 'liga'; + -webkit-font-smoothing: antialiased; + user-select: none; + + &.ms-filled { + font-variation-settings: 'FILL' 1; + } +} \ No newline at end of file diff --git a/sites/www/lib/styles/core/_reset.scss b/sites/www/lib/styles/core/_reset2.scss similarity index 68% rename from sites/www/lib/styles/core/_reset.scss rename to sites/www/lib/styles/core/_reset2.scss index cf9014fece..8180c4cf4d 100644 --- a/sites/www/lib/styles/core/_reset.scss +++ b/sites/www/lib/styles/core/_reset2.scss @@ -7,17 +7,13 @@ html { body, html { height: 100%; - margin: 0; - padding: 0; } -*, +*:not(:where(.post-content *)), *:before, *:after { - box-sizing: border-box; margin-block-start: 0; margin-block-end: 0; - border-width: 0; border-style: solid; border-color: currentColor; } @@ -27,19 +23,7 @@ strong { font-weight: 600; } -button, -input, -optgroup, -select, -textarea { - font-family: inherit; - font-size: 100%; - line-height: inherit; - color: inherit; -} - -ol, -ul { +:is(ol, ul):not(:where(.post-content *)) { list-style: none; margin: 0; padding: 0; @@ -58,4 +42,4 @@ a { button { background-color: transparent; -} +} \ No newline at end of file diff --git a/sites/www/lib/styles/core/_vars.scss b/sites/www/lib/styles/core/_vars.scss index db0d7aae0d..bf67866ec0 100644 --- a/sites/www/lib/styles/core/_vars.scss +++ b/sites/www/lib/styles/core/_vars.scss @@ -1,36 +1,39 @@ +@use 'sass:color'; @use 'breakpoints'; :root { - --blue-10: #041E3C; - --blue-9: #042B59; - --blue-8: #043875; - --blue-7: #0553B1; - --blue-6: #0468D7; /* new primary */ - --primary: #0468D7; /* accessibility */ - --blue-5: #027DFD; + --blue-10: #041E3C; + --blue-9: #042B59; + --blue-8: #043875; + --blue-7: #0553B1; + --blue-6: #0468D7; + /* new primary */ + --primary: #0468D7; + /* accessibility */ + --blue-5: #027DFD; /* --primary: #027DFD; old primary */ - --blue-4: #13B9FD; - --blue-3: #81DDF9; - --blue-2: #B8EAFE; - --blue-1: #E7F8FF; - --pink: #F8BBD0; - --red: #D43324; - --coral: #F25D50; - --yellow: #F2DD22; + --blue-4: #13B9FD; + --blue-3: #81DDF9; + --blue-2: #B8EAFE; + --blue-1: #E7F8FF; + --pink: #F8BBD0; + --red: #D43324; + --coral: #F25D50; + --yellow: #F2DD22; --violet-4: #833EF2; --violet-3: #796CEB; --violet-2: #6200EE; --violet-1: #C6BAFA; - --green-3: #14C2AD; - --green-2: #A7FFEB; - --green-1: #1CDAC5; - --grey-6: #4A4A4A; - --grey-5: #A4A4A4; - --grey-4: #DADCE0; - --grey-3: #E8EAED; - --grey-2: #F1F3F4; - --grey-1: #F8F9FA; - + --green-3: #14C2AD; + --green-2: #A7FFEB; + --green-1: #1CDAC5; + --grey-6: #4A4A4A; + --grey-5: #A4A4A4; + --grey-4: #DADCE0; + --grey-3: #E8EAED; + --grey-2: #F1F3F4; + --grey-1: #F8F9FA; + --ui-anim-func: cubic-bezier(.27, .89, .39, .95); --ui-border-radius: 24px; --ui-border-radius-sm: 8px; @@ -42,10 +45,7 @@ --ui-header-event-banner-height: 50px; --ui-header-navigation-height: 86px; - --ui-header-height: calc( - var(--ui-header-navigation-height) + - var(--ui-header-event-banner-height) - ); + --ui-header-height: calc(var(--ui-header-navigation-height) + var(--ui-header-event-banner-height)); --ui-logo-width: 126px; --font-size-heading-1: 46px; @@ -62,12 +62,44 @@ --font-ui: 'Google Sans Flex', 'Roboto', ui-sans, sans-serif; --font-body: 'Google Sans Flex', 'Roboto', ui-sans, sans-serif; --font-code: 'Google Sans Code', 'Roboto Mono', ui-monospace, monospace; + --site-blog-fontFamily: 'Roboto Serif', 'Noto Serif', var(--font-body); + --site-code-fontFamily: 'Google Sans Code', 'Roboto Mono', ui-monospace, monospace; + --site-icon-fontFamily: 'Material Symbols Outlined', 'Material Symbols', 'Material Icons'; + --site-ui-fontFamily: var(--font-ui); + --site-fontWeight-bold: var(--font-weight-bold); + --site-fontWeight-normal: var(--font-weight-normal); + + --site-outline: rgba(194, 198, 214, .75); + --site-outline-variant: #e7e8ed; + + --site-base-bgColor: #ffffff; + --site-base-fgColor: #212121; + --site-base-fgColor-lighter: #{color.scale(#212121, $lightness: 20%)}; + --site-base-fgColor-alt: #6a6f71; + --site-primary-color: #1967d2; + + --site-inset-bgColor: #f9fafb; + --site-inset-bgColor-translucent: rgba(237, 240, 242, 0.8); + --site-inset-bgColor-translucent-extra: rgba(237, 240, 242, 0.2); + --site-inset-fgColor: var(--site-base-fgColor); + --site-inset-borderColor: #dadce0; - --screen-xs-min: 480px; /* mobile portrait */ - --screen-sm-min: 640px; /* mobile landscape */ - --screen-md-min: 768px; /* tablet portrait */ - --screen-lg-min: 1024px; /* tablet landscape */ - --screen-xl-min: 1280px; /* arguable */ + --site-link-fgColor: var(--site-primary-color); + --site-link-fgColor-active: #0553b1; + + --site-secondaryContainer-bgColor: #c2e5ff; + --site-secondaryContainer-fgColor: var(--site-base-fgColor); + + --screen-xs-min: 480px; + /* mobile portrait */ + --screen-sm-min: 640px; + /* mobile landscape */ + --screen-md-min: 768px; + /* tablet portrait */ + --screen-lg-min: 1024px; + /* tablet landscape */ + --screen-xl-min: 1280px; + /* arguable */ --spacer-sm: 8px; --spacer-md: 32px; @@ -82,15 +114,15 @@ --max-width: calc(var(--screen-xl-min) - (var(--gutter) * 2)); --gutter-auto: calc((100vw - var(--max-width)) / 2); - --grad-blue-green-145: linear-gradient(145deg, #F3FCFF 40%, #D3FFF5 90%); + --grad-blue-green-145: linear-gradient(145deg, #F3FCFF 40%, #D3FFF5 90%); --grad-yellow-blue-145: linear-gradient(145deg, #FAF9C5 40%, #E8F9FF 90%); - --grad-blue-yellow-35: linear-gradient(35deg, #E8F9FF 40%, #FAF9C5 90%); - --grad-yellow-blue-35: linear-gradient(35deg, #FAF9C5 40%, #E8F9FF 90%); - --grad-liteyellow-blue: linear-gradient(35deg, #FCFCE2 40%, #F5FBFE 90%); - --grad-red-blue-35: linear-gradient(35deg, #FCDDE8 0%, #F3FCFF 60%); - --grad-violet-blue-35: linear-gradient(35deg, #E3DDFD 30%, #F3FCFF 90%); - --grad-blue-light-35: linear-gradient(35deg, #DCF5FF 50%, #F3FCFF 90%); - --grad-yellow-blue-75: linear-gradient(75deg, rgba(249, 248, 196, 0.5) 0%, rgba(231, 248, 255, 0.5) 100%); + --grad-blue-yellow-35: linear-gradient(35deg, #E8F9FF 40%, #FAF9C5 90%); + --grad-yellow-blue-35: linear-gradient(35deg, #FAF9C5 40%, #E8F9FF 90%); + --grad-liteyellow-blue: linear-gradient(35deg, #FCFCE2 40%, #F5FBFE 90%); + --grad-red-blue-35: linear-gradient(35deg, #FCDDE8 0%, #F3FCFF 60%); + --grad-violet-blue-35: linear-gradient(35deg, #E3DDFD 30%, #F3FCFF 90%); + --grad-blue-light-35: linear-gradient(35deg, #DCF5FF 50%, #F3FCFF 90%); + --grad-yellow-blue-75: linear-gradient(75deg, rgba(249, 248, 196, 0.5) 0%, rgba(231, 248, 255, 0.5) 100%); @include breakpoints.screen(lg) { /* REFERENCE original sizing @@ -108,9 +140,22 @@ --font-size-heading-5: 16px; --font-size-default: 14px; } + + --site-radius: 0.45rem; +} + +.text-coral { + color: var(--coral) !important; +} + +.text-green { + color: var(--green-3) !important; +} + +.text-blue { + color: var(--blue-4) !important; } -.text-coral { color: var(--coral) !important; } -.text-green { color: var(--green-3) !important; } -.text-blue { color: var(--blue-4) !important; } -.text-violet { color: var(--violet-4) !important; } +.text-violet { + color: var(--violet-4) !important; +} \ No newline at end of file diff --git a/sites/www/lib/styles/pages/_blog_page.scss b/sites/www/lib/styles/pages/_blog_page.scss new file mode 100644 index 0000000000..5a029021bc --- /dev/null +++ b/sites/www/lib/styles/pages/_blog_page.scss @@ -0,0 +1,38 @@ +.blog main { + display: flex; + flex-direction: row; + justify-content: center; + + >article { + width: 60rem; + max-width: 100%; + min-width: 8rem; + + >.content { + padding: 1.5rem; + color: var(--site-base-fgColor) + } + + @media(min-width: 576px) { + >.content { + padding: 2rem + } + } + } + +} + +.blog header { + + .subtype { + padding: 0 0.3rem; + font-size: 1.25rem; + font-weight: 500; + line-height: 1.3; + border-radius: 0.25rem; + background-color: var(--site-secondaryContainer-bgColor); + color: var(--site-secondaryContainer-fgColor); + margin-left: 0.4rem; + letter-spacing: normal; + } +} \ No newline at end of file diff --git a/sites/www/lib/styles/styles.scss b/sites/www/lib/styles/styles.scss index c4dbe3e23c..ea8fb382e1 100644 --- a/sites/www/lib/styles/styles.scss +++ b/sites/www/lib/styles/styles.scss @@ -1,6 +1,7 @@ // Must be imported first to ensure that // the reset is applied before any other styles are applied. -@use 'core/reset'; +@use 'package:site_shared/_sass/base/reset'; +@use 'core/reset2'; // Must be imported first after reset to ensure that our // defined CSS variables are defined before any other @@ -9,9 +10,10 @@ @use 'core/base'; @use 'core/structure'; -@use 'components/button'; +@use 'components/btn'; @use 'components/carousel'; @use 'components/cookies'; +@use 'components/content'; @use 'components/embeds'; @use 'components/features'; @use 'components/filters-dropdown'; @@ -30,6 +32,7 @@ @use 'components/testimonial'; @use 'pages/brand'; +@use 'pages/blog_page'; @use 'pages/buildWithAI'; @use 'pages/community'; @use 'pages/culture'; @@ -47,3 +50,8 @@ @use 'pages/notfound'; @use 'pages/partner'; @use 'pages/showcase'; + +@use 'package:site_shared/_sass/components/breadcrumbs'; +@use 'package:site_shared/_sass/components/blog'; +@use 'package:site_shared/_sass/components/code'; +@use 'package:site_shared/_sass/components/dropdown'; \ No newline at end of file