Flutter UI Widgets

Nov 9 2021 Dart 2.14, Flutter 2.5, VS Code 1.61

Part 1: Flutter UI Widgets

13. Explore Cupertino Widgets

Episode complete

Play next episode

About this episode
See versions

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 12. Make Your App Design Responsive Next episode: 14. Build iOS Styled Forms

This video Explore Cupertino Widgets was last updated on Nov 9 2021

So far, we’ve built our app with material based widgets. Our app would look great on both Android and iOS because material widgets have been adapted for both platforms. We can view how our app would look and feel on different platforms. First lets take note of the default android behaviour. Currently, the appbar title is at the left side of the appbar.

Next, when you scroll past the listview items, you get an overflow glow which matches your theme color. Let’s see the iOS behaviour. Go to main.dart file and set the platform to iOS inside the (light) theme data:

platform: TargetPlatform.iOS,

You would notice some changes. The position of our appbar title moves to the middle for iOS. Let’s scoll past the listview. You can see a bouncing scoll effect. Another change is the route transition.

Comment out the platform code to take it back to the default platform which is android in my case. Back in our app click on the article card once more and you can see a different route transition for android based devices. So you see, material widgets adapts well on both platforms. But Flutter provides us with Cupertino widgets that follows iOS design guidelines.

We have covered the core when it comes to UI widgets but there are some cupertino widgets worth mentioning. To work with cupertino widgets, you could use the CupertinoApp. But we wont be using it becasue of two main reasons.

First: We would still use some material components and some of them rely on values passed down from the MaterialApp. The absense of a Material ancestor could lead to some errors. Secondly, the CupertinoApp has lesser properties like the absense of a darkTheme configuration. Head over to the MainPage widget.

There are two variations of the Scaffold widget for cupertino. We have the CupertinoPageScaffold and CupertinoTabScaffold . CupertinoPageScaffold is just a plain Scaffold that accpets a navigation bar and a child. While the CupertinoTabScaffold comes bundled with tab features.

We would be using both for our example. Replace the MainPage widget with the following content:

class MainPage extends StatelessWidget {
  const MainPage({Key? key}) : super(key: key);

  Widget build(BuildContext context) {
    return Material(
      child: CupertinoTabScaffold(
        tabBar: CupertinoTabBar(
          backgroundColor: CupertinoColors.systemGrey5,
          items: <BottomNavigationBarItem>[
            const BottomNavigationBarItem(
              label: 'Home',
              icon: Icon(CupertinoIcons.home),
            const BottomNavigationBarItem(
              label: 'Inbox',
              icon: Icon(CupertinoIcons.mail),
        tabBuilder: (context, index) {
          switch (index) {
            case 0:
              return CupertinoTabView(builder: (context) {
                return const HomePage();
            case 1:
              return CupertinoTabView(builder: (context) {
                return const InboxPage();
              return Container();

First, we import cupertino for its widgets to work. We also import material because we’re aslo using somematerial based widgets. Next, return a Material widget as the root. This step is necessary if you want to use a material specific widget down the widget tree.

We define a CupertinoTabScaffold and set its items to a list of BottomNavigationBarItem just like we did for the BottomNavigationBar we created previously. We give it a cupertino specific color of systemGrey5 which is a light grey color that conforms to iOS design guideline. For the icons, CupertinoIcons are used. The cupertino_icons package are already included in the pubspec.yaml file of every Flutter stater app.

We also passed in the tabBuilder callback function. This would be called whenever the tabs become active. The switch statement determines which view would be shown based on the tab index. Note: tab contents are cached automatically whenever the tab becomes inactive.

CupertinoTabView are return as the views and they are responsible for creating the contents of the tabs. In this case, that would be the HomePage and the InboxPage. Go ahead and save your work.

And as you can see, the tabs updates with the corresponding views. But notice, we don’t have an appbar. We use the CupertinoPageScaffold for this. Go to the HomePage and InboxPage widgets and wrap them with this widget.

// HomePage and InboxPage
Widget build(BuildContext context) {
  return CupertinoPageScaffold(
    navigationBar: CupertinoNavigationBar(
      middle: Image.asset(
        width: 180,
      // For only HomePage
      trailing: IconButton(
        icon: Icon(CupertinoIcons.add_circled),
        onPressed: () {
      // End
    child: ...

Save all files.

We can see the appbar. Here, we used the CupertinoPageScaffold widget and passed in a CupertinoNavigationBar. We set its middle property to the image asset which is our logo. And if you notice, we’re using the dark version of the logo already provided in the project. The trailing property is an IconButton that would be use to navigate to the CreateArticle page later on.

Let’s navigate to the InboxPage. We can see that we have tge logo and the navbar displayed. Let’s work on the ArticleCard to make it have an iOS feel. Update the the build method of the ArticleCard to the following:

class ArticleCard extends StatelessWidget {
  final Article article;
  final bool isIOS; // New Code

  const ArticleCard({Key key, this.article, this.isIOS = true})
      : super(key: key);

  Widget build(BuildContext context) {
    if (isIOS) {
      return Container(
        margin: const EdgeInsets.all(16),
        padding: const EdgeInsets.only(bottom: 8),
        decoration: const BoxDecoration(
          border: Border(
            bottom: BorderSide(width: 1, color: CupertinoColors.systemGrey4),
        child: WideCard(article: article),
    return Card(

In here, we added a new member: isIOS. This is a boolean that would determine if we want to create an iOS styled card. We set its default value to true. In the build method, we return a Container with a bottom border as the iOS styled card. We replaced the Card with a Container to give it a flat design. Go ahead and save your work. You can see the updated styling. In a future episode, I’ll show you how to get the underlying platform.

We have an overflow error, we’ll solve this later on. Finally, let’s use an replace the android circular spinner with its cupertino counterpart. Head over to the HomePage widget. Scroll to the CircularProgressIndicator. And replace it with the following code:

child: CupertinoActivityIndicator(
  radius: 24,

This is the CupertinoActivityIndicator which is an iOS styled activity indicator. Save your work and do a hot restart. There you have it. Our app now has an iOS feel. Finally, to fix the overflow error. Head over to the CardDetail widget in the aticle card file.

First, remove the spacer widget. Wrap the article source text with the flexible widget. You can see the text flexed to the next line. We want it on one line so add the overflow property and itt to show an ellipsiss. Finally, to add back spaces in between the children of the row, set the mainAxis align to space btw. Save your work and the UI is now fixed.