Flutter Cupertino tutorial: How to build iOS apps that look and feel native - LogRocket Blog (2024)

All frontend apps use some kind of design system to help users accomplish tasks easier. They may use a custom design system that is developed internally or an established design system such as Material Design or Cupertino (iOS).

Flutter Cupertino tutorial: How to build iOS apps that look and feel native - LogRocket Blog (1)

Material design is developed by Google and can be used to develop Android, iOS, web, and desktop apps.

Cupertino is developed by Apple. It is based on Apple’s Human Interface Guidelines, which implement the current iOS design language.

The Flutter SDK comes with both Material and Cupertino widget libraries to develop an app that looks and feels native for either platform.

You can still build an app just using the Material widgets library. However, if you want to build an app that looks like the standard iOS style, you should strongly consider using the Cupertino library.

In this tutorial, we are going to build a simple app that will have three tabs at the bottom; Calls, Chats, and Settings.

On the Calls tab, we’ll add simple Navigation bar; the Chat tab will show a list of members and allow end-users to search for any member; and on the Settings tab, we’ll use various Cupertino-styled widgets to build the settings page.

Here’s what the final app will look like:

Flutter Cupertino tutorial: How to build iOS apps that look and feel native - LogRocket Blog (2)

In this tutorial, we’ll cover:

  • Creating a simple page
  • Adding tabs
  • Adding a NavigationBar that hides on Scrolling List
  • Showing the loading indicator
  • Enabling Search
  • Adding the switch
  • Showing ActionSheet
  • Displaying AlertDialog
  • Adding CupertinoDatePicker

Creating a simple page

Let’s start by creating a simple page that shows the page title at the top and a “Hello” message in the center. To build such a page, you have to delete all the content of the newly created project and replace it with the following code:

import 'package:flutter/cupertino.dart';import 'package:flutter/material.dart';import 'package:flutter/services.dart';import 'simple_page.dart';void main() { WidgetsFlutterBinding.ensureInitialized(); SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, ]).then((value) => runApp(MyApp())); runApp(const MyApp());}class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); // This widget is the root of your application. @override Widget build(BuildContext context) { // 1 <-- SEE HERE return CupertinoApp( // 2 <-- SEE HERE theme: CupertinoThemeData(brightness: Brightness.light), home: CupertinoSimpleHomePage(), ); }}class CupertinoSimpleHomePage extends StatefulWidget { const CupertinoSimpleHomePage({Key? key}) : super(key: key); @override _CupertinoSimpleHomePageState createState() => _CupertinoSimpleHomePageState();}class _CupertinoSimpleHomePageState extends State<CupertinoSimpleHomePage> { @override Widget build(BuildContext context) { // 3 <-- SEE HERE return const CupertinoPageScaffold( // 4 <-- SEE HERE navigationBar: CupertinoNavigationBar( middle: Text('Chat App'), ), child: Center( child: Text('Hi'), ), ); }}

Explanation of code

  • CupertinoApp: The CupertinoApp widget allows you to add widgets that are mostly used to build an iOS styled app
  • CupertinoThemeData: Using this widget, you can specify the styling of your app
  • CupertinoPageScaffold: The CupertinoPageScaffold helps in building the page’s layout, such as adding the navigation bar
  • CupertinoNavigationBar: This widget creates a navigation bar that looks like the native iOS style.

Output

Flutter Cupertino tutorial: How to build iOS apps that look and feel native - LogRocket Blog (3)

Adding tabs

The tabs are used to support the primary navigation of the app. Let’s add three tabs at the bottom, each with a different name and icon. To create tabs, we have to replace CupertinoPageScaffold with CupertinoTabScaffold.

The advantage of using the CupertinoTabScaffold widget is that it allows you to add the tab bar at the bottom of the page, and additionally provides the ability to maintain pages in multiple stacks.

Code

// 1 <-- SEE HEREreturn CupertinoTabScaffold( // 2 <-- SEE HERE tabBar: CupertinoTabBar( currentIndex: 1, items: const <BottomNavigationBarItem>[ // 3 <-- SEE HERE BottomNavigationBarItem( icon: Icon(CupertinoIcons.phone), label: 'Calls'), BottomNavigationBarItem( icon: Icon(CupertinoIcons.chat_bubble_2), label: 'Chats'), BottomNavigationBarItem( icon: Icon(CupertinoIcons.settings), label: 'Settings'), ], ), tabBuilder: (context, index) { late final CupertinoTabView returnValue; switch (index) { case 0: // 4 <-- SEE HERE returnValue = CupertinoTabView(builder: (context) { return const CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( middle: Text('Calls'), ), child: Center(child: Text('Calls'))); }); break; case 1: returnValue = CupertinoTabView( builder: (context) { return CupertinoChatPage(); }, ); break; case 2: returnValue = CupertinoTabView( builder: (context) { return CupertinoSettingsPage(); }, ); break; } return returnValue; },);

Explanation of code

  • CupertinoTabScaffold: The CupertinoTabScaffold widget contains parameters such as tabBar and tabBuilder that allow you to create the tab bar items and the tab bar view
  • CupertinoTabBar: The CupertinoTabBar widget adds the tab bar at the bottom of the screen. It shows multiple items using the widget called BottomNavigationBarItem. The currentIndex property allows you to control the active tab when the app starts
  • BottomNavigationBarItem: This widget lays out an item to display on the tab bar. It contains helpful parameters such as icon, label, and background color to build an item
  • CupertinoTabView: The CupertinoTabView widget is responsible for inflating the content for the selected tab. Each CupertinoTabView has its own navigation stack

Output

Flutter Cupertino tutorial: How to build iOS apps that look and feel native - LogRocket Blog (4)

In the previous steps, we have built a basic setup upon which we can begin to add more widgets.

In the current example, the basic navigation bar always stays on top when scrolling down the list. We can improve the user experience by hiding the navbar when the user starts scrolling.

Over 200k developers use LogRocket to create better digital experiencesLearn more →

Steps

Step 1: Inside the CupertinoTabView, return the CustomScrollView
Step 2: Inside the CustomScrollView, add the CupertinoSliverNavigationBarwidget. This widget hides the navigation bar when scrolling
Step 3: Inside the CupertinoSliverNavigationBar, add the largeTitle parameter to display the navigation title

Code

CupertinoTabView( builder: (context) { return CustomScrollView( slivers: <Widget>[ CupertinoSliverNavigationBar( largeTitle: Text('Chats'), ), ], ); },);

Output

Flutter Cupertino tutorial: How to build iOS apps that look and feel native - LogRocket Blog (7)

Showing the loading indicator

To show the loading indicator, you can use the CupertinoActivityIndicator widget. This widget shows an iOS-style activity indicator that spins in a clockwise direction. Let’s use the CupertinoActivityIndicator with the Text widget to display the “Waiting for network” indication.

Steps

Step 1: Inside the CupertinoSliverNavigationBar, add the middle parameter and assign the Row widget
Step 2: Inside the Row widget, add the CupertinoActivityIndicator
Step 3: Add one more widget (i.e. the Text widget)

Code

CupertinoSliverNavigationBar( largeTitle: Text('Chats'), leading: Text( 'Edit', style: TextStyle(color: CupertinoColors.link), ), middle: Row( mainAxisSize: MainAxisSize.min, children: const [ CupertinoActivityIndicator(), SizedBox(width: 8), Text('Waiting for network') ], ),)

Output

Flutter Cupertino tutorial: How to build iOS apps that look and feel native - LogRocket Blog (8)

Enabling search

Let’s fill the Chat tab with some users and implement the search functionality.

For this, we will:

  • Create a users model class
  • Use it to fill some user data
  • Display using the custom list tile widget
  • Enable search using the CupertinoSearchTextField widget

Steps

Step 1: Create a list of users.

const List<User> users = const <User>[ const User('Jack', Colors.greenAccent), const User('Lucy', Colors.green), const User('Luna', Colors.black26), const User('Oliver', Colors.blue), const User('Lily', Colors.amberAccent), const User('Milo', Colors.purple), const User('Max', Colors.pink), const User('Kitty', Colors.yellowAccent), const User('Simba', Colors.red), const User('Zoe', Colors.blueAccent), const User('Jasper', Colors.deepOrange), const User('Stella', Colors.cyan), const User('Lola', Colors.lightBlue), const User('Halsey', Colors.deepPurpleAccent), const User('Taylor', Colors.indigoAccent),];

Step 2: Copy all users into filteredUsers.

List<User> _filteredUsers = users;

Step 3: Add the SliverGrid widget and use filteredUsers to display a list of users in any scrollable view.

SliverGrid( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 1, childAspectRatio: 5, ), delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { return UserTile(_filteredUsers[index]); }, childCount: _filteredUsers.length, ),)

Step 4: Below the CupertinoSliverNavigationBar, add the SliverToBoxAdapter widget with FractionallySizedBox and ClipRect.

Step 5: Add the CupertinoSearchTextField widget as a child. The CupertinoSearchTextField widget is similar to a normal Textfield widget, but additionally mimics the look and behavior of the iOS style.

SliverToBoxAdapter( child: FractionallySizedBox( widthFactor: 0.9, child: ClipRect( child: Padding( padding: const EdgeInsets.only(top: 16), child: CupertinoSearchTextField( controller: _controller, onChanged: (value) { _updateUserList(value); }, onSubmitted: (value) { _updateUserList(value); }, onSuffixTap: () { _updateUserList(''); }, ), )), ),)

Step 6: Add the _updateUsersList() method to find the users that match the search term.

void _updateUserList(String value) { debugPrint('$value'); if (value.length > 0) { _filteredUsers = _filteredUsers .where((element) => element.name.toLowerCase().contains(value.toLowerCase())) .toList(); } else { _controller.text = ''; _filteredUsers = users; } setState(() {});}

Output

Flutter Cupertino tutorial: How to build iOS apps that look and feel native - LogRocket Blog (9)

Adding the switch

Using the CupertinoSwitch widget, you can create the iOS style switch in your app. Let’s add the CupertinoSwitch widget inside the Settings tab.

Code

CupertinoFormSection( header: Text('Account Details'), children: [ CupertinoFormRow( prefix: Text('Chat Backup'), child: CupertinoSwitch( value: chatBackup, onChanged: (value) { setState(() { chatBackup = !chatBackup; }); }, ), ), ],),

Output

Flutter Cupertino tutorial: How to build iOS apps that look and feel native - LogRocket Blog (10)

Showing ActionSheet

To show an ActionSheet, you can use the CupertinoActionSheet widget. This widget is used to allow users to make a choice between multiple items.

Step 1: Add the CupertinoButton widget.
Step 2: Inside the onPressed method, call the showCupertinoModalPopup.
Step 3: Inside the builder of showCupertinoModalPopup, return CupertinoActionSheet.
Step 4: Inside CupertinoActionSheet, return some actions using the CupertinoActionSheetAction widget.

Code

Center( child: CupertinoButton( onPressed: () { showCupertinoModalPopup<void>( context: context, builder: (BuildContext context) => CupertinoActionSheet( title: const Text('Set Wallpaper Theme'), actions: <CupertinoActionSheetAction>[ CupertinoActionSheetAction( child: const Text('Dark'), onPressed: () { Navigator.pop(context); }, ), CupertinoActionSheetAction( child: const Text('Light'), onPressed: () { Navigator.pop(context); }, ) ], ), ); }, child: const Text('Chat Wallpaper'), ),)

Output

Flutter Cupertino tutorial: How to build iOS apps that look and feel native - LogRocket Blog (11)

Displaying AlertDialog

To show an AlertDialog, you can use the CupertinoAlertDialog widget. The CupertinoAlertDialog widget is used to confirm the user’s action — when deleting an account, for example.

Step 1: Add the CupertinoButton widget.
Step 2: Inside the onPressed method, call showCupertinoDialog.
Step 3: Inside the builder of showCupertinoDialog return CupertinoAlertDialog
Step 4: Inside CupertinoAlertDialog, return some actions using the CupertinoDialogAction widget

Code

Center( child: CupertinoButton( onPressed: () { showCupertinoDialog<void>( context: context, builder: (BuildContext context) => CupertinoAlertDialog( title: const Text('Delete chat'), content: const Text('Proceed with deleting chat?'), actions: <CupertinoDialogAction>[ CupertinoDialogAction( child: const Text('No'), onPressed: () { Navigator.pop(context); }, ), CupertinoDialogAction( child: const Text('Yes'), isDestructiveAction: true, onPressed: () { // Do something destructive. }, ) ], ), ); }, child: const Text('Delete all chat'), ),)

Output

Flutter Cupertino tutorial: How to build iOS apps that look and feel native - LogRocket Blog (12)

Adding CupertinoDatePicker

The CupertinoDatePicker widget allows users to pick a date in the standard iOS style.

Step 1: Add the CupertinoButton widget.
Step 2: Inside the onPressed method, call _showDialog.
Step 3: Return the CupertinoDatePickerwidget with some helpful parameters, such as initialDateTime, mode, and use24hFormat.
Step 4: Add the onDateTimeChanged property and rebuild the widget with a new date.

Code

Center( child: CupertinoButton( // Display a CupertinoDatePicker in date picker mode. onPressed: () => _showDialog( CupertinoDatePicker( backgroundColor: CupertinoColors.white, initialDateTime: date, mode: CupertinoDatePickerMode.date, use24hFormat: true, // This is called when the user changes the date. onDateTimeChanged: (DateTime newDate) { setState(() => date = newDate); }, ), ), // In this example, the date value is formatted manually. You can use intl package // to format the value based on user's locale settings. child: Text( '${date.month}-${date.day}-${date.year}', style: const TextStyle( fontSize: 22.0, ), ), ),)

Output

Flutter Cupertino tutorial: How to build iOS apps that look and feel native - LogRocket Blog (13)

You can find the full source code here.

Conclusion

In this tutorial, we walked through how to build an iOS app that looks and feels native with practical examples.

We used the Cupertino widgets library to build all the visual UI elements such as Switch, ActionSheet, AlertDialog, NavigationBar, and DateTimePicker.

We started with creating a simple page and then saw how you can add search functionality to the app. Later we explored various Cupertino widgets to build the Settings page.

I hope you found this tutorial helpful — happy coding!

Get set up with LogRocket's modern error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to getan app ID
  2. Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, notserver-side

    • npm
    • Script tag
    $ npm i --save logrocket // Code:import LogRocket from 'logrocket'; LogRocket.init('app/id'); 
    // Add to your HTML:<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script><script>window.LogRocket && window.LogRocket.init('app/id');</script> 
  3. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • NgRx middleware
    • Vuex plugin

Get started now

Flutter Cupertino tutorial: How to build iOS apps that look and feel native - LogRocket Blog (2024)
Top Articles
Meet the Man Who Started the Illuminati
Tj Maxx Boston Downtown Crossing
Express Pay Cspire
North Carolina Houses For Rent Craigslist
Order Irs Tax Forms Online
Promiseb Discontinued
Amazon Warehouse Locations - Most Comprehensive List 2023
Craigslist Coeur D'alene Spokane
Humidity Yesterday At My Location
My Happy Feet Shoes Review: How I Finally Got Relief from Years of Heel Pain - 33rd Square
24/7 Walmarts Near Me
What You Need to Know About County Jails
Henry Ford Hospital: Ein Meisterwerk von Frida Kahlo
‘Sound of Freedom’ Is Now Streaming: Here’s Where to Stream the Controversial Crime Thriller Online for Free
Olive Onyx Amora
Chubbs Canton Il
Maya Mixon Portnoy
Myjohnshopkins Mychart
Ultimate Guide to Visiting Dungeness, UK
Binny Arcot
Bones And All Showtimes Near Tucson Spectrum 18
Mchoul Funeral Home Of Fishkill Inc. Services
Www.burlingtonfreepress.com Obituaries
Bearpaws Tropical Weather
New York (NY) Lottery - Winning Numbers & Results
John Wick Megashare
Decree Of Spite Poe
Runnings Milwaukee Tool Sale
Closest Dollar Tree Store To My Location
Busted Paper Haysi Regional Jail
Clash of Clans: Best Hero Equipment For The Archer Queen, Ranked
Lufthansa LH456 (DLH456) from Frankfurt to Los Angeles
Kathy Carrack
Holley Gamble Funeral Home In Clinton
Cece Rose Facial
Sce Menifee Service Center
Rocking Horse Ranch Itinerary
Central Valley growers, undocumented farmworkers condemn Trump's 'emergency'
Craigslist For Port Huron Michigan
North Bay Craigslist Jobs
Hercules 2014 Full Movie Youtube
Ece 2300 Osu
Solar Smash Unblocked Wtf
NCCAC
Diabetes Care - Horizon Blue Cross Blue Shield of New Jersey
Currently Confined Coles County
Ixl Scarsdale
18006548818
Temperature At 12 Pm Today
Fitgirl Starfield
I Only Have Eyes for You by The Flamingos Lyrics Meaning - A Gaze Into Love's Timeless Power - Song Meanings and Facts
Restaurants Near Defy Trampoline Park
Latest Posts
Article information

Author: Rob Wisoky

Last Updated:

Views: 6211

Rating: 4.8 / 5 (68 voted)

Reviews: 91% of readers found this page helpful

Author information

Name: Rob Wisoky

Birthday: 1994-09-30

Address: 5789 Michel Vista, West Domenic, OR 80464-9452

Phone: +97313824072371

Job: Education Orchestrator

Hobby: Lockpicking, Crocheting, Baton twirling, Video gaming, Jogging, Whittling, Model building

Introduction: My name is Rob Wisoky, I am a smiling, helpful, encouraging, zealous, energetic, faithful, fantastic person who loves writing and wants to share my knowledge and understanding with you.