card_settings 0.1.15+1

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

Pub Package Github Issues

Card Settings

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 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
  • 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"

  @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 just the CardSettings heirarchy, 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, wrap different layouts with an `OrientationBuilder'.

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.

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

child: OrientationBuilder(
  builder: (context, orientation) {
    return (orientation == Orientation.portrait)
        ? CardSettings(
          children: <Widget>[
            // Portrait layout here
            CardSettingsEmail(key: _emailKey)
          ],
        )
        : CardSettings(
          children: <Widget>[
            // Landscape layout here
            CardSettingsEmail(key: _emailKey)
          ],
        );
  },
)

You may with to have multiple fields on the same row when in landscape orientation. This required nested wrapper widgets to provide the proper dimensions. This library provides a few shortcut wrappers to produce cleaner code:

  • CardFieldLayout_EqualSpaced - Multiple fields in a row equally spaced
  • CardFieldLayout_FractionallySpaced - Multiple fields in a row with controlled spacing

Usage looks like this:

CardSettings(
  children: <Widget>[
    CardFieldLayout_EqualSpaced(children: <Widget>[
      CardSettingsEmail(),
      CardSettingsPassword(),
    ]),
  ],
);

And to control the relative widths the Fractional version may be used:

CardSettings(
  children: <Widget>[
    CardFieldLayout_FractionallySpaced(
      children: <Widget>[
        CardSettingsEmail(),
        CardSettingsPassword(),
      ],
      widthFactors: <double>[0.75,0.25], // 75% and 25% respectively
    ),
  ],
);

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.

[0.1.0] - 7/26/2018

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

[0.1.1] - 7/26/2018

  • General cleanup to meet publication requirements.

[0.1.2] - 7/27/2018

  • Attempt to improve documentation and remove warnings

[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.4] - 7/31/2018

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

[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.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.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.8] - 8/9/2018

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

[0.1.9-10] - 8/9/2018

  • Removed TextCapitalization from CardSettingsText due to an analysis error

[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.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.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.14] - 8/20/2018

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

[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

example/lib/main.dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:card_settings/card_settings.dart';
import 'package:intl/intl.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
        ),
      ),
    );
  }
}

// example viewmodel for the form
class PonyModel {
  String name = 'Twilight Sparkle';
  String type = 'Unicorn';
  int age = 7;
  String coatColor = 'D19FE4';
  String maneColor = '273873';
  bool hasSpots = false;
  String spotColor = 'FF5198';
  String description =
      'An intelligent and dutiful scholar with an avid love of learning and skill in unicorn magic such as levitation, teleportation, and the creation of force fields.';
  double height = 3.5;
  int weight = 45;
  DateTime showDateTime = DateTime(2010, 10, 10, 20, 30);
  double ticketPrice = 65.99;
  int boxOfficePhone = 8005551212;
  String email = 'me@nowhere.org';
  String password = 'secret1';
}

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

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

  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> _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) {
    return Scaffold(
      key: _scaffoldKey,
      backgroundColor: Theme.of(context).backgroundColor,
      appBar: AppBar(
          title: Text("My Little Pony"),
          actions: <Widget>[
            IconButton(
              icon: Icon(Icons.save),
              onPressed: _savePressed,
            ),
          ],
          leading: IconButton(
            icon: Icon(Icons.refresh),
            onPressed: _resetPressed,
          )),
      body: Form(
        key: _formKey,
        child: Theme(
          data: Theme.of(context).copyWith(
            primaryTextTheme: TextTheme(
              title:
                  TextStyle(color: Colors.lightBlue[50]), // style for headers
            ),
            inputDecorationTheme: InputDecorationTheme(
              labelStyle:
                  TextStyle(color: Colors.indigo[400]), // style for labels
            ),
          ),
          child: OrientationBuilder(
            builder: (context, orientation) {
              return (orientation == Orientation.portrait)
                  ? _buildPortraitLayout()
                  : _buildLandscapeLayout();
            },
          ),
        ),
      ),
    );
  }

  /* CARDSETTINGS FOR EACH LAYOUT */

  CardSettings _buildPortraitLayout() {
    return CardSettings(
      children: <Widget>[
        CardSettingsHeader(label: 'Bio'),
        _buildCardSettingsText_Name(_nameKey),
        _buildCardSettingsListPicker_Type(_typeKey),
        _buildCardSettingsNumberPicker(_ageKey),
        _buildCardSettingsParagraph(_descriptionlKey, 5),
        CardSettingsHeader(label: 'Colors'),
        _buildCardSettingsColorPicker_Coat(_coatKey),
        _buildCardSettingsColorPicker_Mane(_maneKey),
        _buildCardSettingsSwitch_Spots(_hasSpotsKey),
        _buildCardSettingsColorPicker_Spot(_spotKey),
        CardSettingsHeader(label: 'Size'),
        _buildCardSettingsDouble_Height(_heightKey),
        _buildCardSettingsInt_Weight(_weightKey),
        CardSettingsHeader(label: 'First Show'),
        _buildCardSettingsInstructions(),
        _buildCardSettingsDatePicker(_dateKey),
        _buildCardSettingsTimePicker(_timeKey),
        _buildCardSettingsCurrency(_priceKey),
        _buildCardSettingsPhone(_phoneKey),
        CardSettingsHeader(label: 'Security'),
        _buildCardSettingsEmail(_emailKey),
        _buildCardSettingsPassword(_passwordKey),
        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(_nameKey),
        CardFieldLayout_FractionallySpaced(children: <Widget>[
          _buildCardSettingsListPicker_Type(_typeKey),
          _buildCardSettingsNumberPicker(_ageKey, labelAlign: TextAlign.right),
        ], widthFactors: <double>[
          0.7,
          0.3
        ]),
        _buildCardSettingsParagraph(_descriptionlKey, 3),
        // note, different order than portrait to show state mapping
        CardSettingsHeader(label: 'Security'),
        CardFieldLayout_EqualSpaced(children: <Widget>[
          _buildCardSettingsEmail(_emailKey),
          _buildCardSettingsPassword(_passwordKey),
        ]),
        CardSettingsHeader(label: 'Colors'),
        CardFieldLayout_EqualSpaced(children: <Widget>[
          _buildCardSettingsColorPicker_Coat(_coatKey),
          _buildCardSettingsColorPicker_Mane(_maneKey),
        ]),
        CardFieldLayout_EqualSpaced(children: <Widget>[
          _buildCardSettingsSwitch_Spots(_hasSpotsKey),
          _buildCardSettingsColorPicker_Spot(_spotKey),
        ]),
        CardSettingsHeader(label: 'Size'),
        CardFieldLayout_EqualSpaced(children: <Widget>[
          _buildCardSettingsDouble_Height(_heightKey),
          _buildCardSettingsInt_Weight(_weightKey),
        ]),
        CardSettingsHeader(label: 'First Show'),
        _buildCardSettingsInstructions(),
        CardFieldLayout_EqualSpaced(children: <Widget>[
          _buildCardSettingsDatePicker(_dateKey),
          _buildCardSettingsTimePicker(_timeKey),
        ]),
        CardFieldLayout_EqualSpaced(children: <Widget>[
          _buildCardSettingsCurrency(_priceKey),
          _buildCardSettingsPhone(_phoneKey),
        ]),
        CardSettingsHeader(label: 'Actions'),
        CardFieldLayout_EqualSpaced(children: <Widget>[
          _buildCardSettingsButton_Save(),
          _buildCardSettingsButton_Reset(),
          _buildCardSettingsButton_Close(),
        ]),
        Container(height: 4.0)
      ],
    );
  }

  /* BUILDERS FOR EACH FIELD */

  CardSettingsButton _buildCardSettingsButton_Close() {
    return CardSettingsButton(
      label: 'CLOSE',
      onPressed: _closePressed,
      backgroundColor: Colors.greenAccent,
    );
  }

  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(Key key) {
    return CardSettingsPassword(
      key: key,
      icon: Icon(Icons.lock),
      initialValue: _ponyModel.password,
      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(Key key) {
    return CardSettingsEmail(
      key: key,
      icon: Icon(Icons.person),
      initialValue: _ponyModel.email,
      validator: (value) {
        if (value == null || value.isEmpty) return 'Email is required.';
        if (!value.contains('@')) return "Email not formatted correctly.";
        return null;
      },
      onSaved: (value) => _ponyModel.email = value,
      onChanged: (value) => _showSnackBar('Email', value),
    );
  }

  CardSettingsPhone _buildCardSettingsPhone(Key key) {
    return CardSettingsPhone(
      key: key,
      label: 'Box Office',
      initialValue: _ponyModel.boxOfficePhone,
      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(Key key) {
    return CardSettingsCurrency(
      key: key,
      label: 'Ticket Price',
      initialValue: _ponyModel.ticketPrice,
      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(Key key) {
    return CardSettingsTimePicker(
      key: key,
      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(Key key) {
    return CardSettingsDatePicker(
      key: key,
      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(Key key) {
    return CardSettingsInt(
      key: key,
      label: 'Weight',
      unitLabel: 'lbs',
      initialValue: _ponyModel.weight,
      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(Key key) {
    return CardSettingsDouble(
      key: key,
      label: 'Height',
      unitLabel: 'feet',
      initialValue: _ponyModel.height,
      onSaved: (value) => _ponyModel.height = value,
      onChanged: (value) => _showSnackBar('Height', value),
    );
  }

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

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

  CardSettingsColorPicker _buildCardSettingsColorPicker_Mane(Key key) {
    return CardSettingsColorPicker(
      key: key,
      label: 'Mane',
      initialValue: intelligentCast<Color>(_ponyModel.maneColor),
      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(Key key) {
    return CardSettingsColorPicker(
      key: key,
      label: 'Coat',
      initialValue: intelligentCast<Color>(_ponyModel.coatColor),
      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),
    );
  }

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

  CardSettingsNumberPicker _buildCardSettingsNumberPicker(Key key,
      {TextAlign labelAlign}) {
    return CardSettingsNumberPicker(
      key: key,
      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(Key key) {
    return CardSettingsListPicker(
      key: key,
      label: 'Type',
      initialValue: _ponyModel.type,
      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(Key key) {
    return CardSettingsText(
      key: key,
      label: 'Name',
      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();
    } else {
      setState(() => _autoValidate = true);
    }
  }

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

  void _closePressed() {
    // This is just a placeholder to have a third button
  }

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

  void _showResults() {
    showDialog<String>(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text('Updated Results'),
          content: SingleChildScrollView(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                _buildResultsRow('Name', _ponyModel.name),
                _buildResultsRow('Type', _ponyModel.type),
                _buildResultsRow('Age', _ponyModel.age),
                Text(_ponyModel.description),
                _buildResultsRow('CoatColor', _ponyModel.coatColor),
                _buildResultsRow('ManeColor', _ponyModel.maneColor),
                _buildResultsRow('HasSpots', _ponyModel.hasSpots),
                _buildResultsRow('SpotColor', _ponyModel.spotColor),
                _buildResultsRow('Height', _ponyModel.height),
                _buildResultsRow('Weight', _ponyModel.weight),
                _buildResultsRow('ShowDate',
                    DateFormat.yMd().format(_ponyModel.showDateTime)),
                _buildResultsRow('ShowTime',
                    DateFormat.jm().format(_ponyModel.showDateTime)),
                _buildResultsRow('Phone', _ponyModel.boxOfficePhone),
                _buildResultsRow('Price', _ponyModel.ticketPrice),
              ],
            ),
          ),
          actions: <Widget>[
            FlatButton(
              child: Text("Close"),
              onPressed: () {
                Navigator.of(context).pop();
              },
            ),
          ],
        );
      },
    );
  }

  Widget _buildResultsRow(String name, dynamic value) {
    return Column(
      children: <Widget>[
        Row(
          children: <Widget>[
            Expanded(
              child: Text(
                '$name:',
                style: TextStyle(fontWeight: FontWeight.bold),
              ),
            ),
            Text(value.toString()),
          ],
        ),
        Container(height: 12.0),
      ],
    );
  }
}

Use this package as a library

1. Depend on it

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


dependencies:
  card_settings: ^0.1.15+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
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
0.1.13 Aug 19, 2018 Go to the documentation of card_settings 0.1.13 Download card_settings 0.1.13 archive
0.1.12 Aug 18, 2018 Go to the documentation of card_settings 0.1.12 Download card_settings 0.1.12 archive
0.1.11 Aug 16, 2018 Go to the documentation of card_settings 0.1.11 Download card_settings 0.1.11 archive
0.1.10 Aug 9, 2018 Go to the documentation of card_settings 0.1.10 Download card_settings 0.1.10 archive
0.1.9 Aug 9, 2018 Go to the documentation of card_settings 0.1.9 Download card_settings 0.1.9 archive
0.1.8 Aug 9, 2018 Go to the documentation of card_settings 0.1.8 Download card_settings 0.1.8 archive

All 18 versions...

Popularity:
Describes how popular the package is relative to other packages. [more]
76
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]
88
Learn more about scoring.

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

  • Dart: 2.0.0
  • pana: 0.11.8
  • Flutter: 0.5.7

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Suggestions

Package is pre-v1 release.

While there is nothing inherently wrong with versions of 0.*.*, it usually means that the author is still experimenting with the general direction of the API.

Fix lib/helpers/scroll_picker.dart.

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

line 156 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 >=1.19.0 <3.0.0
flutter 0.0.0
flutter_colorpicker ^0.0.7 0.0.7
flutter_masked_text ^0.6.0 0.6.0
intl ^0.15.6 0.15.7
Transitive dependencies
collection 1.14.6 1.14.11
meta 1.1.5 1.1.6
path 1.6.2
sky_engine 0.0.99
typed_data 1.1.5 1.1.6
vector_math 2.0.6 2.0.8
Dev dependencies
flutter_test