Background
Flutter 3.27.0 arrived with a bang, bringing a wave of improvements to both performance and developer experience. This release shows how Flutter is always getting better and helping developers make great apps.
In this article, we’ll explore the key updates of the Cupertino and Material widgets in Flutter 3.27.0
Ready? Let’s dive in!
SliverFloatingHeader
Just like Google Photos, The SliverFloatingHeader
is a widget designed to create headers that appear when the user scrolls forward and gracefully disappear when scrolling in the opposite direction.
CustomScrollView(
slivers: [
SliverFloatingHeader(
child: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.orange,
),
child: const Text(
"Sliver Floating Header like Google photos",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
)),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return ListTile(
title: Text("Item $index"),
tileColor: index.isEven? Colors.grey.shade300 : Colors.grey.shade100,
);
},
childCount: 100,
),
),
],
);
Tubular Figures
Have you ever faced UI misalignment when displaying monospaced text like DateTime
, such as 01:00 PM
or 02:30 PM
? I encountered this issue while building the Canbook app, where I needed to display time slots. Initially, I tried calculating the width based on the thickest character (0
) to fix the alignment.
How can we improve the UI for rendering monospaced text like DateTime
and time
, and maintain alignment and consistency for dynamic text updates?
In the old UI implementation, digits had variable widths due to the default font settings. This resulted in inconsistent alignment.
SingleChildScrollView(
child: Wrap(
alignment: WrapAlignment.start,
crossAxisAlignment: WrapCrossAlignment.center,
spacing: 8,
runSpacing: 8,
children: List.generate(30, (index) {
final time = DateTime.now().add(Duration(minutes: (index) * 15));
return Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
border: Border.all(
color: Colors.white,
),
borderRadius: BorderRadius.circular(8),
),
child: Text(
DateFormat('hh:mm a').format(time),
style: const TextStyle(
fontFeatures: [FontFeature.tabularFigures()],
color: Colors.white),
),
);
}),
),
);
FontFeature.tabularFigures()
feature ensures that each character occupies the same width.
This feature enables the use of tabular figures for fonts with both proportional (varying width) and tabular figures. Tabular figures are monospaced, meaning all characters have the same width, making them ideal for aligning text in tables, lists, or dynamic UI elements like time slots or numeric data.
Text(
DateFormat('hh:mm a').format(time),
style: const TextStyle(
fontFeatures: [FontFeature.tabularFigures()],
color: Colors.white),
),
CupertinoSliverNavigationBar
With the latest update, both CupertinoNavigationBar
and CupertinoSliverNavigationBar
now feature transparent backgrounds until content scrolls underneath them.
This enables,
- The navigation bar to match the background color in its expanded state.
- A customizable color in its collapsed state.
- Smooth transitions between these colors as the user scrolls.
CustomScrollView(
slivers: [
const CupertinoSliverNavigationBar(
largeTitle : Text(
'Cupertino Transparent Navigation Bar',
style: TextStyle(color: Colors.white, fontSize: 20),
),
backgroundColor: Colors.transparent,
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return ListTile(
title: Text("Item $index"),
);
},
childCount: 100,
),
),
],
);
Repeat Animation
Flutter’s repeat
method allows animations to run infinitely. But what if you want the animation to run for a specific number of times?
Now, with the addition of the count
parameter, the repeat
method can restart the animation and perform a set number of iterations before stopping.
If no value is provided for count
, the animation will repeat indefinitely.
@override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
animation = Tween<double>(begin: 100, end: 300).animate(controller)
..addListener(() {
setState(() {});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
width: animation.value,
height: animation.value,
color: Colors.yellow),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
controller.repeat(count: 2);
},
label: Text("Start Animation"),
),
);
}
Row and Column Spacing
With the release of Flutter 3.27.0, Row
and Column
widgets now support the spacing parameter, eliminating the need for manual SizedBox or Padding to add spacing
between child widgets.
Now, with Flutter 3.27, you can simplify the layout by directly using the spacing
property.
Using SizedBox
for spacing between children,
@override
Widget build(BuildContext context) {
return Column(
children: [
_item(),
const SizedBox(height: 16),
_item(),
const SizedBox(height: 16),
_item(),
const SizedBox(height: 16),
_item(),
const SizedBox(height: 16),
_item(),
],
);
}
With Updates,
@override
Widget build(BuildContext context) {
return Column(
spacing: 16,
children: [
_item(),
_item(),
_item(),
_item(),
],
);
}
Tap to scroll to the item in CupertinoPicker
CupertinoPicker
now supports tapping on an item to automatically scroll to it, to navigate directly to the selected item.
Refresh indicator
Refresh Indicator with No Spinner
RefreshIndicator.noSpinner
widget to create a refresh indicator without the default spinner. This allows you to handle the refresh action with custom UI elements while still using the drag-to-refresh functionality.
Stack(
children: [
RefreshIndicator.noSpinner(
onRefresh: () async {
isRefreshing = true;
setState(() {});
await Future.delayed(const Duration(seconds: 2));
isRefreshing = false;
setState(() {});
},
child: ListView()
),
if (isRefreshing)
Align(
child: CircularProgressIndicator(
color: Colors.orange,
),
),
],
),
Elevation
The elevation
property determines the shadow depth of the RefreshIndicator
. The default value is 2.0
.
RefreshIndicator(
elevation: 10,
backgroundColor: Colors.orange,
onRefresh: () async {
await Future.delayed(const Duration(seconds: 2));
}, // Callback function for refresh action
child: ListView()
),
With enhancements in both design and functionality, this release makes building beautiful, cross-platform apps easier than ever.
So, Curious to explore the full breakdown of these updates…?
Head over to our full blog at Canopas to explore all the new key updates in Flutter 3.27.0! 🚀
If you like what you read, be sure to hit 💖 button! — as a writer it means the world!
I encourage you to share your thoughts in the comments section below. Your input not only enriches our content but also fuels our motivation to create more valuable and informative articles for you.
Happy coding!👋
Top comments (0)