Leave a rating/review
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);
@override
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();
});
default:
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
...
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Image.asset(
'assets/images/logo_dark.png',
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);
@override
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.