card_settings 1.2.1

  • README.md
  • CHANGELOG.md
  • Example
  • Installing
  • Versions
  • 92

Card Settings

Pub Package Github Issues

A flutter package for building card based settings forms. This includes a library of pre-built form field widgets. The style is a bit like a cross between the cupertino settings screen and material design; The idea is it should be usable and intutive on both iOS and Android.

Screenshot

This package consists of a CardSettings layout wrapper and a series of form field options including:

  • Text Fields
    • CardSettingsText - Basic text field
    • CardSettingsParagraph - Multiline text field with a counter
    • CardSettingsEmail - A text field pre-configured for email input
    • CardSettingsPassword - A text field pre-configured for passwords
    • CardSettingsPhone - A masked phone entry field (US style currently)
  • Numeric Fields
    • CardSettingsDouble - Field for double precision numbers
    • CardSettingsInt - Field for integer numbers
    • CardSettingsCurrency - Field for currency entry
    • CardSettingsSwitch - Field for boolean state
  • Pickers
    • CardSettingsListPicker - Picker list of arbitrary options
    • CardSettingsNumberPicker - Picker list of numbers in a given range
    • CardSettingsColorPicker - RGB Color Picker
    • CardSettingsDatePicker - Material Design Date Picker
    • CardSettingsTimePicker - Material Design Time Picker
  • Selection
    • CardSettingsMultiselect - Select from a list of available options
  • Informational Sections
    • CardSettingsHeader - A control to put a header between form sections
    • CardSettingsInstructions - Informational read-only text
  • Actions
    • CardSettingsButton - Actions buttons for the form

All fields support validate, onChange, onSaved, autovalidate, and visible.

The package also includes these additonal items:

  • CardSettingsField - The base layout widget. You can use this to build custom fields
  • Converters - a set of utility functions to assist in converting data into and out of the fields

Simple Example

All fields in this package are compatible with the standard Flutter Form widget. Simply wrap the CardSettings control in a form and use it as you normally would with the form functionality.

  String title = "Spheria";
  String author = "Cody Leet";
  String url = "http://www.codyleet.com/spheria"

  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
      body: Form(
        key: _formKey,
        child: CardSettings(
          children: <Widget>[
            CardSettingsHeader(label: 'Favorite Book'),
            CardSettingsText(
              label: 'Title',
              initialValue: title,
              validator: (value) {
                if (value == null || value.isEmpty) return 'Title is required.';
              },
              onSaved: (value) => title = value,
            ),
            CardSettingsText(
              label: 'URL',
              initialValue: url,
              validator: (value) {
                if (!value.startsWith('http:')) return 'Must be a valid website.';
              },
              onSaved: (value) => url = value,
            ),
          ],
        ),
      ),
    );
  }

See the full demo example here.

Theming

The widgets support the material design theme. This example shows what global theme values to set to determine how the various elements appear.

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Card Settings Example',
      home: new HomePage(),
      theme: ThemeData(
        accentColor: Colors.indigo[400], // used for card headers
        cardColor: Colors.white, // used for field backgrounds
        backgroundColor: Colors.indigo[100], // color outside the card
        primaryColor: Colors.teal, // color of page header
        buttonColor: Colors.lightBlueAccent[100], // background color of buttons
        textTheme: TextTheme(
          button: TextStyle(color: Colors.deepPurple[900]), // style of button text
          subhead: TextStyle(color: Colors.deepOrange[900]), // style of input text
        ),
      ),
    );
  }
}

Or if you want to apply a different theme to the CardSettings hierarchy only, you can wrap it in a Theme widget like so:

  Theme(
    data: Theme.of(context).copyWith(
      primaryTextTheme: TextTheme(
        title: TextStyle(color: Colors.lightBlue[50]), // style for headers
      ),
      inputDecorationTheme: InputDecorationTheme(
        labelStyle: TextStyle(color: Colors.deepPurple), // style for labels
      ),
    ),
    child: CardSettings(
      ...
    ),
  )

Global Properties

The CardSettings widget implements a few global settings that all child fields can inherit. Currently it supports only label customization.

Labels

You can control how the labels are rendered with four properties:

  CardSettings(
    labelAlign: TextAlign.right, // change the label alignment
    labelSuffix: ':', // add an optional tag after the label
    labelPadding: 10.0, // control the spacing between the label and the content
    contentAlign: TextAlign.left, // alignment of the entry widgets
    icon: Icon(Icons.person), // puts and option icon to the left of the label
    requiredIndicator: Text('*', style: TextStyle(color: Colors.red)), // puts an optional indicator to the right of the label
  )

The labelAlign and contentAlign properties are also available on each field, so you can override the global setting for individual fields.

  CardSettingsText(
    label: 'Last Name',
    labelAlign: TextAlign.left,
    contentAlign: TextAlign.right,
  )

Dynamic Visibility

Each field implements a visible property that you can use to control the visibility based on the value of other fields. In this example, the switch field controls the visibility of the text field:

  bool _ateOut = false;

  CardSettingsSwitch(
    label: 'Ate out?',
    initialValue: _ateOut,
    onChanged: (value) => setState(() => _ateOut = value),
  ),

  CardSettingsText(
    label: 'Restaurant',
    visible: _ateOut,
  ),

Masking

The CardSettingsText widget has an inputMask property that forces entered text to conform to a given pattern. This is built upon the flutter_masked_text package and as such masks are formatted with the following characters:

  • 0: accept numbers
  • A: accept letters
  • @: accept numbers and letters
  • *: accept any character

So for example, phone number would be '(000) 000-0000'.

Note: CardSettingsPhone is a convenience widget that is pre-configured to use this pattern.

Note2: flutter_masked_text is a controller and as such, you will not be able to use an inputMask and a custom controller at the same time. This might be rectified in the future.

Orientation

This suite allows for orientation switching. To configure this, build different layouts depending on the orientation provided by MediaQuery.

You might want to have different fields in each layout, or a different field order. So that Flutter doesn't get confused tracking state under this circumstance, you must provide a unique state key for each individual field, using the same key in each layout.

@override
Widget build(BuildContext context) {

  final GlobalKey<FormState> _emailKey = GlobalKey<FormState>();
  var orientation = MediaQuery.of(context).orientation;

  return Form
    key: _formKey,(
    child: (orientation == Orientation.portraitUp)
        ? CardSettings(children: <Widget>[
              // Portrait layout here
              CardSettingsEmail(key: _emailKey)
            ])
        : CardSettings(children: <Widget>[
              // Landscape layout here
              CardSettingsEmail(key: _emailKey)
            ]);
    },
  );
}

You may have multiple fields on the same row in landscape orientation. This normally requires the use of container widgets to provide the layout inside the row. Instead, you can use the CardFieldLayout helper widget to streamline this. It will by default make it's children equally spaced, but you can provide an array of flex values to control the relative sizes.

// equally spaced example
CardSettings(
  children: <Widget>[
    CardFieldLayout(children: <Widget>[
      CardSettingsEmail(),
      CardSettingsPassword(),
    ]),
  ],
);
// relative width example
CardSettings(
  children: <Widget>[
    CardFieldLayout_FractionallySpaced(
      children: <Widget>[
        CardSettingsEmail(),
        CardSettingsPassword(),
      ],
      flexValues: [2, 1], // 66% and 33% widths
    ),
  ],
);

Custom Fields

The CardSettingsField is the basis of all other fields and can be used to build unique fields outside of this library. Its purpose is to govern layout with consistent decorations. The best way to make a custom field is to inherit from FormField<T>, which will manage the state of your field. The cleanest example of this is the CardSettingsSwitch widget. All you have to do is provide your custom widgets in the content property.

Dependencies

This widget set relies on these external third-party components:

Changelog

Please see the Changelog page to know what's recently changed.

Contributions

Feel free to contribute to this project.

If you find a bug or want a feature, but don't know how to fix/implement it, please fill an issue.

If you fixed a bug or implemented a new feature, please send a pull request.

If you want to discuss this project, please join the Discord chat.

Releases

[1.2.1] - 10/2/2018

  • Removed hard coded word 'Select' from picker title
  • Added text capitalization to CardSettingsText
  • Set picker to first item if no initialvalue is provided
  • Added 'hintText' to CardListPicker

[1.2.0] - 9/8/2018

  • Fixed overflow bug with picker dialogs on smaller screens
  • Refactored row layout helpers into a single flexible CardFieldLayout

[1.1.0] - 9/5/2018

  • Added hintText to CardSettingsText widget

[1.0.1] - 8/24/2018

  • Changed Example to use NativeDeviceOrientationReader for orientation changes.

[1.0.0] - 8/24/2018

  • Ready for full release. No API changes predicted
  • Added Discord channel to the collaboration section

[0.1.16] - 8/23/2018

  • Added CardSettingsMultiselect widget
  • Fixed bug when onChange was null
  • Fixed validation issue with phone numbers

[0.1.15] - 8/20/2018

  • Added landscape layout and material title for CardSettingsListPicker
  • Added landscape layout and material title for CardSettingsNumberPicker
  • Added landscape layout and material title for CardSettingsColorPicker

[0.1.14] - 8/20/2018

  • Added CardFieldLayout_FractionallySpaced for controlled spacing in a row
  • Fixed bug with labelAlign right not working

[0.1.13] - 8/19/2018

  • Enhanced the example to show switching to landscape orientation
  • Created CardFieldLayout_EqualSpaced to handle mutiple fields in a row.

[0.1.12] - 8/17/2018

  • Added padding and cardElevation to CardSettings
  • Added support for field icon to every widget
  • Added a requiredIndicator to show next to a label
  • Removed textInputAction property to be compatible with the current beta branch

[0.1.11] - 8/15/2018

  • downgraded intl dependency to ^0.15.6
  • removed a few properties that were reported as issues in dartpub analyze

[0.1.10] - 8/9/2018

  • Removed TextCapitalization from CardSettingsText due to an analysis error

[0.1.8] - 8/9/2018

  • Added a analysis_options.yaml file and a bunch if linter checks
  • Cleaned and tigtened code

[0.1.7] - 8/8/2018

  • All fields now implement an onChange event.
  • Added a CardSettingsPhone widget
  • Enhanced CardSettingsText to allow a input mask (based on flutter_masked_text)

[0.1.6] - 8/2/2018

  • Added contentAlign property to all fields to allow for right justification
  • Added labelAlign to CardHeaders to allow center or right positioning
  • Improved support of themes for input text and labels

[0.1.5] - 8/1/2018

  • Support default button style through themes
  • All text fields expose controller as optional parameter (except currency)
  • Support theming of header text
  • Changed CardSettings to an InheritableWidget with global properties to control label appearance
  • Added a labelAlign property to all fields

[0.1.4] - 7/31/2018

  • Added CardSettingsEmail field
  • Added CardSettingsPassword field
  • Changed all TextFormFields to be stateless widget wrappers

[0.1.3] - 7/30/2018

  • Added CardSettingsCurrency field
  • Added CardSettingsInstructions field
  • Added CardSettingsButton field
  • Added ability to tap to select in pickers

[0.1.2] - 7/27/2018

  • Attempt to improve documentation and remove warnings

[0.1.1] - 7/26/2018

  • General cleanup to meet publication requirements.

[0.1.0] - 7/26/2018

  • First release implementing card view and core set of field widgets.

example/lib/main.dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:card_settings/card_settings.dart';
import 'results.dart';
import 'model.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Card Settings Example',
      home: PonyExample(),
      theme: ThemeData(
        accentColor: Colors.indigo[400], // background color of card headers
        cardColor: Colors.white, // background color of fields
        backgroundColor: Colors.indigo[100], // color outside the card
        primaryColor: Colors.teal, // color of page header
        buttonColor: Colors.lightBlueAccent[100], // background color of buttons
        textTheme: TextTheme(
          button:
              TextStyle(color: Colors.deepPurple[900]), // style of button text
          subhead: TextStyle(color: Colors.grey[800]), // style of input text
        ),
        primaryTextTheme: TextTheme(
          title: TextStyle(color: Colors.lightBlue[50]), // style for headers
        ),
        inputDecorationTheme: InputDecorationTheme(
          labelStyle: TextStyle(color: Colors.indigo[400]), // style for labels
        ),
      ),
    );
  }
}

class PonyExample extends StatefulWidget {
  @override
  _PonyExampleState createState() => _PonyExampleState();
}

class _PonyExampleState extends State<PonyExample> {
  final _ponyModel = PonyModel();

  // once the form submits, this is flipped to true, and fields can then go into autovalidate mode.
  bool _autoValidate = false;

  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

  // control state only works if the field order never changes.
  // to support orientation changes, we assign a unique key to each field
  // if you only have one orientation, the _formKey is sufficient
  final GlobalKey<FormState> _nameKey = GlobalKey<FormState>();
  final GlobalKey<FormState> _typeKey = GlobalKey<FormState>();
  final GlobalKey<FormState> _ageKey = GlobalKey<FormState>();
  final GlobalKey<FormState> _descriptionlKey = GlobalKey<FormState>();
  final GlobalKey<FormState> _hobbiesKey = GlobalKey<FormState>();
  final GlobalKey<FormState> _coatKey = GlobalKey<FormState>();
  final GlobalKey<FormState> _maneKey = GlobalKey<FormState>();
  final GlobalKey<FormState> _hasSpotsKey = GlobalKey<FormState>();
  final GlobalKey<FormState> _spotKey = GlobalKey<FormState>();
  final GlobalKey<FormState> _heightKey = GlobalKey<FormState>();
  final GlobalKey<FormState> _weightKey = GlobalKey<FormState>();
  final GlobalKey<FormState> _dateKey = GlobalKey<FormState>();
  final GlobalKey<FormState> _timeKey = GlobalKey<FormState>();
  final GlobalKey<FormState> _priceKey = GlobalKey<FormState>();
  final GlobalKey<FormState> _phoneKey = GlobalKey<FormState>();
  final GlobalKey<FormState> _emailKey = GlobalKey<FormState>();
  final GlobalKey<FormState> _passwordKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    var orientation = MediaQuery.of(context).orientation;

    return Scaffold(
      key: _scaffoldKey,
      backgroundColor: Theme.of(context).backgroundColor,
      appBar: appBar(),
      body: Form(
        key: _formKey,
        child: (orientation == Orientation.portrait)
            ? _buildPortraitLayout()
            : _buildLandscapeLayout(),
      ),
    );
  }

  AppBar appBar() => AppBar(
        title: Text("My Little Pony"),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.save),
            onPressed: _savePressed,
          ),
        ],
        leading: IconButton(
          icon: Icon(Icons.refresh),
          onPressed: _resetPressed,
        ),
      );

  /* CARDSETTINGS FOR EACH LAYOUT */

  CardSettings _buildPortraitLayout() {
    return CardSettings(
      children: <Widget>[
        CardSettingsHeader(label: 'Bio'),
        _buildCardSettingsText_Name(),
        _buildCardSettingsListPicker_Type(),
        _buildCardSettingsNumberPicker(),
        _buildCardSettingsParagraph(5),
        _buildCardSettingsMultiselect(),
        CardSettingsHeader(label: 'Colors'),
        _buildCardSettingsColorPicker_Coat(),
        _buildCardSettingsColorPicker_Mane(),
        _buildCardSettingsSwitch_Spots(),
        _buildCardSettingsColorPicker_Spot(),
        CardSettingsHeader(label: 'Size'),
        _buildCardSettingsDouble_Height(),
        _buildCardSettingsInt_Weight(),
        CardSettingsHeader(label: 'First Show'),
        _buildCardSettingsInstructions(),
        _buildCardSettingsDatePicker(),
        _buildCardSettingsTimePicker(),
        _buildCardSettingsCurrency(),
        _buildCardSettingsPhone(),
        CardSettingsHeader(label: 'Security'),
        _buildCardSettingsEmail(),
        _buildCardSettingsPassword(),
        CardSettingsHeader(label: 'Actions'),
        _buildCardSettingsButton_Save(),
        _buildCardSettingsButton_Reset(),
        Container(height: 4.0)
      ],
    );
  }

  CardSettings _buildLandscapeLayout() {
    return CardSettings(
      labelPadding: 12.0,
      children: <Widget>[
        CardSettingsHeader(label: 'Bio'),
        _buildCardSettingsText_Name(),
        CardFieldLayout(
          <Widget>[
            _buildCardSettingsListPicker_Type(),
            _buildCardSettingsNumberPicker(labelAlign: TextAlign.right),
          ],
          flexValues: [2, 1],
        ),
        _buildCardSettingsParagraph(3),
        _buildCardSettingsMultiselect(),
        // note, different order than portrait to show state mapping
        CardSettingsHeader(label: 'Security'),
        CardFieldLayout(<Widget>[
          _buildCardSettingsEmail(),
          _buildCardSettingsPassword(),
        ]),
        CardSettingsHeader(label: 'Colors'),
        CardFieldLayout(<Widget>[
          _buildCardSettingsColorPicker_Coat(),
          _buildCardSettingsColorPicker_Mane(),
        ]),
        CardFieldLayout(<Widget>[
          _buildCardSettingsSwitch_Spots(),
          _buildCardSettingsColorPicker_Spot(),
        ]),
        CardSettingsHeader(label: 'Size'),
        CardFieldLayout(<Widget>[
          _buildCardSettingsDouble_Height(),
          _buildCardSettingsInt_Weight(),
        ]),
        CardSettingsHeader(label: 'First Show'),
        _buildCardSettingsInstructions(),
        CardFieldLayout(<Widget>[
          _buildCardSettingsDatePicker(),
          _buildCardSettingsTimePicker(),
        ]),
        CardFieldLayout(<Widget>[
          _buildCardSettingsCurrency(),
          _buildCardSettingsPhone(),
        ]),
        CardSettingsHeader(label: 'Actions'),
        CardFieldLayout(<Widget>[
          _buildCardSettingsButton_Save(),
          _buildCardSettingsButton_Reset(),
        ]),
        Container(height: 4.0)
      ],
    );
  }

  /* BUILDERS FOR EACH FIELD */

  CardSettingsButton _buildCardSettingsButton_Reset() {
    return CardSettingsButton(
      label: 'RESET',
      onPressed: _resetPressed,
      backgroundColor: Colors.redAccent,
      textColor: Colors.white,
    );
  }

  CardSettingsButton _buildCardSettingsButton_Save() {
    return CardSettingsButton(
      label: 'SAVE',
      onPressed: _savePressed,
    );
  }

  CardSettingsPassword _buildCardSettingsPassword() {
    return CardSettingsPassword(
      key: _passwordKey,
      icon: Icon(Icons.lock),
      initialValue: _ponyModel.password,
      autovalidate: _autoValidate,
      validator: (value) {
        if (value == null) return 'Password is required.';
        if (value.length <= 6) return 'Must be more than 6 characters.';
        return null;
      },
      onSaved: (value) => _ponyModel.password = value,
      onChanged: (value) => _showSnackBar('Password', value),
    );
  }

  CardSettingsEmail _buildCardSettingsEmail() {
    return CardSettingsEmail(
      key: _emailKey,
      icon: Icon(Icons.person),
      initialValue: _ponyModel.email,
      autovalidate: _autoValidate,
      validator: (value) {
        if (value == null || value.isEmpty) return 'Email is required.';
        if (!value.contains('@'))
          return "Email not formatted correctly."; // use regex in real application
        return null;
      },
      onSaved: (value) => _ponyModel.email = value,
      onChanged: (value) => _showSnackBar('Email', value),
    );
  }

  CardSettingsPhone _buildCardSettingsPhone() {
    return CardSettingsPhone(
      key: _phoneKey,
      label: 'Box Office',
      initialValue: _ponyModel.boxOfficePhone,
      autovalidate: _autoValidate,
      validator: (value) {
        if (value != null && value.toString().length != 10)
          return 'Incomplete number';
        return null;
      },
      onSaved: (value) => _ponyModel.boxOfficePhone = value,
      onChanged: (value) => _showSnackBar('Box Office', value),
    );
  }

  CardSettingsCurrency _buildCardSettingsCurrency() {
    return CardSettingsCurrency(
      key: _priceKey,
      label: 'Ticket Price',
      initialValue: _ponyModel.ticketPrice,
      autovalidate: _autoValidate,
      validator: (value) {
        if (value != null && value > 100) return 'No scalpers allowed!';
        return null;
      },
      onSaved: (value) => _ponyModel.ticketPrice = value,
      onChanged: (value) => _showSnackBar('Ticket Price', value),
    );
  }

  CardSettingsTimePicker _buildCardSettingsTimePicker() {
    return CardSettingsTimePicker(
      key: _timeKey,
      icon: Icon(Icons.access_time),
      label: 'Time',
      initialValue: TimeOfDay(
          hour: _ponyModel.showDateTime.hour,
          minute: _ponyModel.showDateTime.minute),
      onSaved: (value) => _ponyModel.showDateTime =
          updateJustTime(value, _ponyModel.showDateTime),
      onChanged: (value) => _showSnackBar('Show Time', value),
    );
  }

  CardSettingsDatePicker _buildCardSettingsDatePicker() {
    return CardSettingsDatePicker(
      key: _dateKey,
      icon: Icon(Icons.calendar_today),
      label: 'Date',
      initialValue: _ponyModel.showDateTime,
      onSaved: (value) => _ponyModel.showDateTime =
          updateJustDate(value, _ponyModel.showDateTime),
      onChanged: (value) => _showSnackBar('Show Date', value),
    );
  }

  CardSettingsInstructions _buildCardSettingsInstructions() {
    return CardSettingsInstructions(
      text: 'This is when this little horse got her big break',
    );
  }

  CardSettingsInt _buildCardSettingsInt_Weight() {
    return CardSettingsInt(
      key: _weightKey,
      label: 'Weight',
      unitLabel: 'lbs',
      initialValue: _ponyModel.weight,
      autovalidate: _autoValidate,
      validator: (value) {
        if (value != null) {
          if (value > 70) return 'You won\'t fly at the weight.';
          if (value < 10) return 'Cmon, you are not a feather.';
        }
        return null;
      },
      onSaved: (value) => _ponyModel.weight = value,
      onChanged: (value) => _showSnackBar('Weight', value),
    );
  }

  CardSettingsDouble _buildCardSettingsDouble_Height() {
    return CardSettingsDouble(
      key: _heightKey,
      label: 'Height',
      unitLabel: 'feet',
      initialValue: _ponyModel.height,
      onSaved: (value) => _ponyModel.height = value,
      onChanged: (value) => _showSnackBar('Height', value),
    );
  }

  CardSettingsColorPicker _buildCardSettingsColorPicker_Spot() {
    return CardSettingsColorPicker(
      key: _spotKey,
      label: 'Spot',
      initialValue: intelligentCast<Color>(_ponyModel.spotColor),
      visible: _ponyModel.hasSpots,
      onSaved: (value) => _ponyModel.spotColor = colorToString(value),
      onChanged: (value) => _showSnackBar('Spot', value),
    );
  }

  CardSettingsSwitch _buildCardSettingsSwitch_Spots() {
    return CardSettingsSwitch(
      key: _hasSpotsKey,
      label: 'Has Spots?',
      initialValue: _ponyModel.hasSpots,
      onChanged: (value) {
        setState(() => _ponyModel.hasSpots = value);
        _showSnackBar('Has Spots?', value);
      },
      onSaved: (value) => _ponyModel.hasSpots = value,
    );
  }

  CardSettingsColorPicker _buildCardSettingsColorPicker_Mane() {
    return CardSettingsColorPicker(
      key: _maneKey,
      label: 'Mane',
      initialValue: intelligentCast<Color>(_ponyModel.maneColor),
      autovalidate: _autoValidate,
      validator: (value) {
        if (value.computeLuminance() > .7) return 'This color is too light.';
        return null;
      },
      onSaved: (value) => _ponyModel.maneColor = colorToString(value),
      onChanged: (value) => _showSnackBar('Mane', value),
    );
  }

  CardSettingsColorPicker _buildCardSettingsColorPicker_Coat() {
    return CardSettingsColorPicker(
      key: _coatKey,
      label: 'Coat',
      initialValue: intelligentCast<Color>(_ponyModel.coatColor),
      autovalidate: _autoValidate,
      validator: (value) {
        if (value.computeLuminance() < .3)
          return 'This color is not cheery enough.';
        return null;
      },
      onSaved: (value) => _ponyModel.coatColor = colorToString(value),
      onChanged: (value) => _showSnackBar('Coat', value),
    );
  }

  CardSettingsMultiselect _buildCardSettingsMultiselect() {
    return CardSettingsMultiselect(
      key: _hobbiesKey,
      label: 'Hobbies',
      initialValues: _ponyModel.hobbies,
      options: allHobbies,
      onSaved: (value) => _ponyModel.hobbies = value,
      onChanged: (value) => _showSnackBar('Hobbies', value),
    );
  }

  CardSettingsParagraph _buildCardSettingsParagraph(int lines) {
    return CardSettingsParagraph(
      key: _descriptionlKey,
      label: 'Description',
      initialValue: _ponyModel.description,
      numberOfLines: lines,
      onSaved: (value) => _ponyModel.description = value,
      onChanged: (value) => _showSnackBar('Description', value),
    );
  }

  CardSettingsNumberPicker _buildCardSettingsNumberPicker(
      {TextAlign labelAlign}) {
    return CardSettingsNumberPicker(
      key: _ageKey,
      label: 'Age',
      labelAlign: labelAlign,
      initialValue: _ponyModel.age,
      min: 1,
      max: 30,
      validator: (value) {
        if (value == null) return 'Age is required.';
        if (value > 20) return 'No grown-ups allwed!';
        if (value < 3) return 'No Toddlers allowed!';
        return null;
      },
      onSaved: (value) => _ponyModel.age = value,
      onChanged: (value) => _showSnackBar('Age', value),
    );
  }

  CardSettingsListPicker _buildCardSettingsListPicker_Type() {
    return CardSettingsListPicker(
      key: _typeKey,
      label: 'Type',
      initialValue: _ponyModel.type,
      hintText: 'Select One',
      autovalidate: _autoValidate,
      options: <String>['Earth', 'Unicorn', 'Pegasi', 'Alicorn'],
      validator: (String value) {
        if (value == null || value.isEmpty) return 'You must pick a type.';
        return null;
      },
      onSaved: (value) => _ponyModel.type = value,
      onChanged: (value) => _showSnackBar('Type', value),
    );
  }

  CardSettingsText _buildCardSettingsText_Name() {
    return CardSettingsText(
      key: _nameKey,
      label: 'Name',
      hintText: 'something cute...',
      initialValue: _ponyModel.name,
      requiredIndicator: Text('*', style: TextStyle(color: Colors.red)),
      autovalidate: _autoValidate,
      validator: (value) {
        if (value == null || value.isEmpty) return 'Name type is required.';
        return null;
      },
      onSaved: (value) => _ponyModel.name = value,
      onChanged: (value) => _showSnackBar('Name', value),
    );
  }

  /* EVENT HANDLERS */

  Future _savePressed() async {
    final form = _formKey.currentState;

    if (form.validate()) {
      form.save();
      showResults(context, _ponyModel);
    } else {
      setState(() => _autoValidate = true);
    }
  }

  void _resetPressed() {
    _formKey.currentState.reset();
  }

  void _showSnackBar(String label, dynamic value) {
    _scaffoldKey.currentState.removeCurrentSnackBar();
    _scaffoldKey.currentState.showSnackBar(
      SnackBar(
        content: Text(label + ' = ' + value.toString()),
      ),
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  card_settings: ^1.2.1

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter packages get

Alternatively, your editor might support flutter packages get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:card_settings/card_settings.dart';
  
Version Uploaded Documentation Archive
1.2.1 Oct 2, 2018 Go to the documentation of card_settings 1.2.1 Download card_settings 1.2.1 archive
1.2.0 Sep 10, 2018 Go to the documentation of card_settings 1.2.0 Download card_settings 1.2.0 archive
1.1.0 Sep 5, 2018 Go to the documentation of card_settings 1.1.0 Download card_settings 1.1.0 archive
1.0.1 Aug 24, 2018 Go to the documentation of card_settings 1.0.1 Download card_settings 1.0.1 archive
1.0.0 Aug 24, 2018 Go to the documentation of card_settings 1.0.0 Download card_settings 1.0.0 archive
0.1.16 Aug 23, 2018 Go to the documentation of card_settings 0.1.16 Download card_settings 0.1.16 archive
0.1.15+1 Aug 21, 2018 Go to the documentation of card_settings 0.1.15+1 Download card_settings 0.1.15+1 archive
0.1.15 Aug 21, 2018 Go to the documentation of card_settings 0.1.15 Download card_settings 0.1.15 archive
0.1.14 Aug 20, 2018 Go to the documentation of card_settings 0.1.14 Download card_settings 0.1.14 archive
0.1.13+1 Aug 19, 2018 Go to the documentation of card_settings 0.1.13+1 Download card_settings 0.1.13+1 archive

All 24 versions...

Popularity:
Describes how popular the package is relative to other packages. [more]
84
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
92
Learn more about scoring.

We analyzed this package on Oct 10, 2018, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.0.0
  • pana: 0.12.4
  • Flutter: 0.9.5

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Health suggestions

Fix lib/helpers/scroll_picker.dart. (-0.50 points)

Analysis of lib/helpers/scroll_picker.dart reported 1 hint:

line 155 col 35: The member 'activity' can only be used within instance members of subclasses of 'ScrollPosition'.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.0.0-dev.63.0.flutter-4c9689c1d2 <3.0.0
flutter 0.0.0
flutter_colorpicker ^0.0.7 0.0.7 0.1.0
flutter_masked_text ^0.6.0 0.6.0
intl ^0.15.6 0.15.7
meta ^1.1.6 1.1.6
Transitive dependencies
collection 1.14.11
path 1.6.2
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test