DEV Community

Cover image for Exploring Cupertino and Material Updates in Flutter 3.27.0
Nandani Sharma for Canopas Software

Posted on • Originally published at canopas.com

Exploring Cupertino and Material Updates in Flutter 3.27.0

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,
      ),
    ),
  ],
);
Enter fullscreen mode Exit fullscreen mode

SliverFloatingHeader

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),
        ),
      );
    }),
  ),
);
Enter fullscreen mode Exit fullscreen mode

Inconsistent Alignment

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),
),
Enter fullscreen mode Exit fullscreen mode

FontFeatures with tabularFigures

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,
      ),
    ),
  ],
);
Enter fullscreen mode Exit fullscreen mode

CupertinoSliverNavigationBar

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"),
    ),
  );
}

Enter fullscreen mode Exit fullscreen mode

Repeat Animation with count

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(),
    ],
  );
}
Enter fullscreen mode Exit fullscreen mode

With Updates,

@override
  Widget build(BuildContext context) {
    return Column(
      spacing: 16,
      children: [
        _item(),
        _item(),
        _item(),
        _item(),
      ],
    );
  }
Enter fullscreen mode Exit fullscreen mode

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.

CupertinoPicker with AutoScroll

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,
        ),
      ),
  ],
),
Enter fullscreen mode Exit fullscreen mode

RefreshIndicator without Spinner

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()
      ),
Enter fullscreen mode Exit fullscreen mode

Refresh Indicator With 0 Elevation

Refresh Indicator With 10 Elevation


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)