Language is a way to communicate our thoughts in a better way. Similarly, there exists a design system language of some kind in the frontend world. It helps users identify what it is and what to do next. Overall it improves the User experience to a great extent.

Talking about more specific to the mobile world, There are main two types of design systems.

  1. Material design: Its created by Google to build high-quality digital experiences for most of the frontend platform.
  2. Cupertino (iOS) design: It’s based on Apple’s Human Interface Guideline to implement the current iOS design language.

You are free to implement the Material design for all your apps targetting Android, iOS, and the web. It will give a consistent user experience across all devices and browsers. But if you have got more time or you want your iOS users to feel at home by providing an iOS-style design to the app (Just like WhatsApp), you would need the Cupertino widgets library to do that. Flutter has already got you covered for this. You can see the list of all Cupertino widgets here.

As Flutter is becoming the fastest growing toolkit to develop apps, the demand for having more Cupertino widgets increased. With the release of Flutter 2.0, the team has launched some new Cupertino widgets.

In this tutorial, we will cover the below new Cupertino widgets in Flutter.

  • CupertinoSearchTextField
  • CupertinoFormSection
  • CupertinoFormRow
  • CupertinoTextFormFieldRow

CupertinoSearchTextField

CupertinoSearchTextField is a widget that allows a user to enter the search term. It is consist of TextField, Search Icon and Cancel button. CupertinoSearchTextField does not perform the search itself but allows us a way to get the search term and process it further.

Below is the minimal code to show CupertinoSearchTextField.

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

class CupertionSearchWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: SafeArea(
        child: Container(
          padding: EdgeInsets.all(16),
          child: CupertinoSearchTextField(),
        ),
    ));
  }
}

And it looks like this…

Let’s look at some properties to customize the CupertinoSearchTextField.

Changing the background

  • backgroundColor: This allows us to change the background color of the search field.
CupertinoSearchTextField(
  backgroundColor: Colors.orange,
)
backgroundColor
  • decoration: It is used to provide any custom decoration to the text field.
CupertinoSearchTextField(
  decoration: BoxDecoration(
    gradient: LinearGradient(
      begin: FractionalOffset.topCenter,
      end: FractionalOffset.bottomCenter,
      colors: [
        Colors.teal.withOpacity(0.0),
        Colors.teal.withOpacity(0.8),
      ],
    ),
  ),
)

Changing the placeholder and its style

  • placeholder: It helps to change the placeholder text from ‘Search’ (Which is the default) to whatever you like.
CupertinoSearchTextField(
  placeholder: 'Find your product',
)
placeholder
  • placeholderStyle: You can also change the style of placeholder text such as color, size, weight, etc.
CupertinoSearchTextField(
  backgroundColor: Colors.orange,
  placeholderStyle: TextStyle(color: Colors.white.withOpacity(0.8)),
)
placeholderStyle

Changing the style for search text

  • style: To change the style of the text when the search term is entered into the search box, Use this parameter. It accepts the TextStyleand it has got a lot of customization options.
CupertinoSearchTextField(
  style: TextStyle(color: Colors.teal, fontWeight: FontWeight.bold),
)
style

Modifying search and cancel icon

  • itemSize: This is used to change the size of the prefix icon (search icon) and suffix icon (cancel button).
CupertinoSearchTextField(
  itemSize: 30,
)
  • itemColor: The color of the search and cancel icon can be changed using this property.
CupertinoSearchTextField(
  itemColor: Colors.red,
)
  • suffixIcon: This helps in changing the default cancel icon.
CupertinoSearchTextField(
  suffixIcon: Icon(Icons.cancel_presentation),
)

Making the rounded corner search box

  • borderRadius: To make the corners of the search field more rounded, Setting the borderRadius to appropriate value will do the job.
CupertinoSearchTextField(
  borderRadius: BorderRadius.all(Radius.circular(20)),
)

Receiving input text

The input text can be received via onChanged and onSubmit callback.

CupertinoSearchTextField(
  onChanged: (value) {
    print("The changed text is " + value);
  },
  onSubmitted: (value) {
    print("Submitted text is " + value);
  },
)

Let’s try to build a working example

Our goal is to filter the list of products as the user enters the product name inside the CupertinoSearchTextField. Also, the user should be able to see the full list with the click of the cancel icon.

Step 1: Creating a list of products.

const List<Product> products = const <Product>[
  const Product('Samsung S5', Colors.greenAccent),
  const Product('Nokia M3', Colors.green),
  const Product('Samsung S8', Colors.white),
  const Product('iPhone X', Colors.blue),
  const Product('iPhone Xr', Colors.amberAccent),
  const Product('Samsung S9', Colors.purple),
  const Product('iPhone 11', Colors.pink),
  const Product('iPhone 11 pro', Colors.yellowAccent),
  const Product('iPhone 12', Colors.red),
  const Product('iPhone 12 mini', Colors.blueAccent),
  const Product('Nokia M2', Colors.deepOrange),
  const Product('iPhone 11 pro max', Colors.cyan),
  const Product('Motorola X1', Colors.lightBlue),
  const Product('Motorola X2', Colors.deepPurpleAccent),
  const Product('Motorola X5', Colors.indigoAccent),
];

Step 2: Copy all products into the filteredProducts. filteredProducts is just another variable to show a filtered product list.

List<Product> _filteredProducts = products;

Step 3: Use the filteredProducts to display a list of products in any kind of list view.

GridView.builder(
  itemCount: _filteredProducts.length,
  itemBuilder: (context, index) =>
      ProductTile(_filteredProducts[index]),
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: constraints.maxWidth > 700 ? 4 : 1,
    childAspectRatio: 5,
  ),
);

Step 4: Place a CupertinoSearchTextField as shown in the below code above list of products inside the widget tree.

CupertinoSearchTextField(
  controller: _controller,
  style: TextStyle(color: Colors.white),
  onChanged: (value) {
    _updateProductList(value);
  },
  onSubmitted: (value) {
    _updateProductList(value);
  },
  onSuffixTap: () {
    _updateProductList('');
  },
)

Step 5: Implementing _updateProductList() method to find the product that matches with the search term.

void _updateProductList(String value) {

  if (value.length > 0) {
    _filteredProducts = _filteredProducts
        .where((element) =>
            element.name.toLowerCase().contains(value.toLowerCase()))
        .toList();
  } else {
    _controller.text = '';
    _filteredProducts = products;
  }

  setState(() {});
}

And here is the output.

CupertinoFormSection

CupertinoFormSection is a widget that creates a standard iOS form.

The basic code to show CupertinoFormSection looks like this.

CupertinoFormSection(
  header: Text('Account Details'),
  children: [
    Text('Text 1'),
    Text('Text 2'),
    Text('Text 3'),
    Text('Text 4')
  ],
)

Output:

You can also create standard “Inset Grouped” iOS forms using CupertinoFormSection.insetGrouped constructor. This creates a rounded shaped form.

CupertinoFormSection.insetGrouped(
  header: Text('Account Details'),
  children: [
    Text('Text 1'),
    Text('Text 2'),
    Text('Text 3'),
    Text('Text 4')
  ],
)

Output:

CupertinoFormRow

CupertinoFormRow is a widget used to create an iOS style form row that consists of prefix and child widget.

The sample code looks like this

CupertinoFormSection(
  header: Text('Account Details'),
  children: [
    CupertinoFormRow(
      prefix: Text('Name'),
      child: Text('Good name here'),
    ),
  ],
)

Output:

It has also got the error widget which can be very helpful in showing validation errors when you have any input widget in the child property. The basic example is entering a password.

CupertinoFormRow(
  child: CupertinoTextField(
    obscureText: true,
  ),
  prefix: Text("Password"),
  error: Text('Must be 8 digit long'),
)

Output:

CupertinoTextFormFieldRow

CupertinoTextFormFieldRow is a combination of three widgets.

  • CupertinoFormRow 
  • FormField
  • CupertinoTextField

At its heart, it’s just a CupertinoTextField with CupertinoFormRow and FormField wrapped over it. Here is how to use it.

CupertinoFormRow(
  prefix: Text('Mobile'),
  child: CupertinoTextFormFieldRow(
    placeholder: "Enter contact",
    keyboardType: TextInputType.phone,
  ),
  helper: Text('Without country code'),
)

Output:

Hers is the code to build a complete form using CupertinoFormSection, CupertinoFormRow, and CupertinoTextFormFieldRow.

SingleChildScrollView(
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      CupertinoFormSection(
        header: Text('Account Details'),
        children: [
          CupertinoFormRow(
            prefix: Text('Name'),
            child: CupertinoTextFormFieldRow(
              placeholder: "Enter name",
            ),
          ),
          CupertinoFormRow(
            prefix: Text("Email"),
            child: Text('pinkesh.earth@gmail.com'),
          ),
          CupertinoFormRow(
            prefix: Text('Mobile'),
            child: CupertinoTextFormFieldRow(
              placeholder: "Enter contact",
              keyboardType: TextInputType.phone,
            ),
            helper: Text('Without country code'),
          ),
          CupertinoFormRow(
            child: CupertinoTextFormFieldRow(
              obscureText: true,
            ),
            prefix: Text("Password"),
            error: Text('Must be 8 digit long'),
          ),
        ],
        footer: Text('Last updated 1 month ago'),
      ),
      CupertinoFormSection(
        header: Text('Social accounts'),
        children: [
          CupertinoFormRow(
            prefix: Icon(Icons.wifi),
            child: CupertinoSwitch(
              value: true,
              onChanged: (value) {},
            ),
          ),
          CupertinoFormRow(
            prefix: Text('Google'),
            child: CupertinoSwitch(
              value: true,
              onChanged: (value) {},
            ),
          ),
          CupertinoFormRow(
            prefix: Text("Github"),
            child: CupertinoSwitch(
              value: false,
              onChanged: (value) {},
            ),
          ),
        ],
      )
    ],
  ),
)

Output:

Conclusion

In this tutorial, we learned some new Cupertino widgets and saw how to customize them as per our needs.