iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Cupertino (iOS)

CupertinoApp + Cupertino widgets: iOS-style UI in Flutter. When to mix with Material, and the platform-aware patterns.

Flutter — Cupertino widgets

EXAMPLE
import 'package:flutter/cupertino.dart';

// ===== A Cupertino app =====
void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return CupertinoApp(
      title: 'Cupertino',
      theme: const CupertinoThemeData(brightness: Brightness.light),
      home: const Home(),
    );
  }
}

// ===== Page with NavigationBar =====
class Home extends StatelessWidget {
  const Home({super.key});
  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: const CupertinoNavigationBar(
        middle: Text('Home'),
        trailing: Icon(CupertinoIcons.search),
      ),
      child: SafeArea(
        child: Column(
          children: [
            const SizedBox(height: 16),
            CupertinoButton.filled(
              child: const Text('Tap me'),
              onPressed: () {},
            ),
            const SizedBox(height: 12),
            CupertinoTextField(placeholder: 'Search...'),
          ],
        ),
      ),
    );
  }
}

// ===== Tabs =====
class TabsApp extends StatelessWidget {
  const TabsApp({super.key});
  @override
  Widget build(BuildContext context) {
    return CupertinoTabScaffold(
      tabBar: CupertinoTabBar(items: const [
        BottomNavigationBarItem(icon: Icon(CupertinoIcons.home), label: 'Home'),
        BottomNavigationBarItem(icon: Icon(CupertinoIcons.settings), label: 'Settings'),
      ]),
      tabBuilder: (ctx, i) => CupertinoTabView(
        builder: (ctx) => i == 0 ? const Home() : const Settings(),
      ),
    );
  }
}

class Settings extends StatelessWidget {
  const Settings({super.key});
  @override
  Widget build(BuildContext context) => const Center(child: Text('Settings'));
}

// ===== Common widgets =====
// CupertinoButton, CupertinoButton.filled
// CupertinoTextField
// CupertinoSwitch, CupertinoSlider
// CupertinoPicker (wheel picker)
// CupertinoDatePicker
// CupertinoAlertDialog, CupertinoActionSheet
// CupertinoActivityIndicator (spinner)
// CupertinoSegmentedControl
// CupertinoListSection, CupertinoListTile

// ===== Dialogs + action sheets =====
showCupertinoDialog(
  context: context,
  builder: (ctx) => CupertinoAlertDialog(
    title: const Text('Delete?'),
    content: const Text('This cannot be undone.'),
    actions: [
      CupertinoDialogAction(child: const Text('Cancel'), onPressed: () => Navigator.pop(ctx)),
      CupertinoDialogAction(isDestructiveAction: true, child: const Text('Delete'), onPressed: () { /* ... */ Navigator.pop(ctx); }),
    ],
  ),
);

// ===== Platform-aware pattern =====
// Show Material on Android, Cupertino on iOS:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart' as material;

Widget platformApp() {
  if (defaultTargetPlatform == TargetPlatform.iOS) {
    return const MyApp();   // CupertinoApp
  }
  return material.MaterialApp(home: const Home());
}

// Or use packages like flutter_platform_widgets to abstract.

// ===== When Cupertino wins =====
// - iOS-first apps where you want native look
// - App store reviewers expecting iOS UI conventions
// - Apple Watch / iPad apps

// ===== When Material is fine =====
// - Cross-platform apps with consistent design language
// - You want one widget set across both platforms
// - Internal tools, prototypes, MVPs

// ===== Patterns to internalise =====
// - One design system per app: pick Material or Cupertino, do not mix
// - CupertinoIcons (not Icons) for iOS look
// - SafeArea + CupertinoPageScaffold at the root
// - flutter_platform_widgets if you genuinely need both per platform

// ===== Pitfalls =====
// - Mixing Cupertino + Material widgets randomly
// - Forgetting that Material vs Cupertino theme data are SEPARATE
// - Custom dark mode handling — CupertinoThemeData differs from ThemeData
// - showCupertinoDialog from a Material ancestor — works but visually inconsistent

Why it matters

CupertinoApp + Cupertino widgets give you iOS-flavoured UI without writing Swift. Pick Cupertino or Material at the app level, lean on CupertinoPageScaffold + SafeArea + CupertinoIcons. Mix only when there is a specific reason; design system consistency is the dial users feel.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
import 'package:flutter/cupertino.dart';
CupertinoApp(
    home: CupertinoPageScaffold(
        navigationBar: const CupertinoNavigationBar(middle: Text('iOS')),
        child: const Center(child: Text('Hi')),
    ),
);
Try it Yourself »

Discussion

Loading…