quantity_stepper 0.1.0 copy "quantity_stepper: ^0.1.0" to clipboard
quantity_stepper: ^0.1.0 copied to clipboard

Accessible quantity stepper for Flutter: screen-reader adjust actions, 48dp targets, long-press repeat, keyboard support, a real FormField. No dependencies.

example/lib/main.dart

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

void main() => runApp(const ExampleApp());

/// Shows the main ways of using [QuantityStepper].
class ExampleApp extends StatelessWidget {
  /// Creates the example app.
  const ExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'quantity_stepper',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF1B7F5C)),
        useMaterial3: true,
      ),
      home: const ExamplePage(),
    );
  }
}

/// The single page of the example.
class ExamplePage extends StatefulWidget {
  /// Creates the page.
  const ExamplePage({super.key});

  @override
  State<ExamplePage> createState() => _ExamplePageState();
}

class _ExamplePageState extends State<ExamplePage> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();

  bool _rtl = false;
  bool _wheel = false;
  bool _announce = false;

  int _basic = 2;
  int _guests = 1;
  int _editable = 12;
  String _formResult = 'Not submitted yet.';

  @override
  Widget build(BuildContext context) {
    return Directionality(
      textDirection: _rtl ? TextDirection.rtl : TextDirection.ltr,
      child: Scaffold(
        appBar: AppBar(title: const Text('quantity_stepper')),
        body: ListView(
          padding: const EdgeInsets.all(16),
          children: <Widget>[
            SwitchListTile(
              title: const Text('Right-to-left layout'),
              value: _rtl,
              onChanged: (bool v) => setState(() => _rtl = v),
            ),
            SwitchListTile(
              title: const Text('Mouse wheel (needs focus)'),
              value: _wheel,
              onChanged: (bool v) => setState(() => _wheel = v),
            ),
            SwitchListTile(
              title: const Text('Announce changes'),
              value: _announce,
              onChanged: (bool v) => setState(() => _announce = v),
            ),
            const Divider(height: 32),
            _Section(
              title: 'Basic',
              caption:
                  'Between 1 and 10. Tap, hold to repeat, or focus and use '
                  'ArrowUp / ArrowDown / PageUp / PageDown.',
              child: Row(
                children: <Widget>[
                  QuantityStepper(
                    label: 'Quantity',
                    min: 1,
                    max: 10,
                    initialValue: _basic,
                    enableMouseWheel: _wheel,
                    announceChanges: _announce,
                    onChanged: (int v) => setState(() => _basic = v),
                  ),
                  const SizedBox(width: 16),
                  Text('Selected: $_basic'),
                ],
              ),
            ),
            _Section(
              title: 'In a form',
              caption:
                  'A real FormField<int>: validator, onSaved, autovalidate '
                  'and reset.',
              child: Form(
                key: _formKey,
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Row(
                      children: <Widget>[
                        const Text('Guests'),
                        const SizedBox(width: 16),
                        QuantityStepperFormField(
                          label: 'Guests',
                          initialValue: 1,
                          max: 8,
                          autovalidateMode: AutovalidateMode.onUserInteraction,
                          enableMouseWheel: _wheel,
                          announceChanges: _announce,
                          semanticFormatter: (int n) =>
                              n == 1 ? '1 guest' : '$n guests',
                          validator: (int? value) => (value ?? 0) < 2
                              ? 'Add at least two guests.'
                              : null,
                          onSaved: (int? value) => _guests = value ?? 1,
                        ),
                      ],
                    ),
                    const SizedBox(height: 12),
                    Wrap(
                      spacing: 8,
                      crossAxisAlignment: WrapCrossAlignment.center,
                      children: <Widget>[
                        FilledButton(
                          onPressed: () {
                            final FormState form = _formKey.currentState!;
                            if (form.validate()) {
                              form.save();
                              setState(() => _formResult = 'Saved: $_guests');
                            } else {
                              setState(
                                () => _formResult = 'Fix the errors first.',
                              );
                            }
                          },
                          child: const Text('Save'),
                        ),
                        OutlinedButton(
                          onPressed: () {
                            _formKey.currentState!.reset();
                            setState(() => _formResult = 'Reset.');
                          },
                          child: const Text('Reset'),
                        ),
                        Text(_formResult),
                      ],
                    ),
                  ],
                ),
              ),
            ),
            _Section(
              title: 'Vertical',
              caption: 'axis: Axis.vertical, with a step of 5.',
              child: QuantityStepper(
                label: 'Boxes',
                axis: Axis.vertical,
                step: 5,
                max: 50,
                enableMouseWheel: _wheel,
                announceChanges: _announce,
              ),
            ),
            _Section(
              title: 'Editable',
              caption:
                  'Type a number. A value in range applies as you type; out '
                  'of range is clamped when you press Done or leave the field.',
              child: Row(
                children: <Widget>[
                  QuantityStepper(
                    label: 'Pages',
                    editable: true,
                    min: 1,
                    max: 500,
                    step: 1,
                    largeStep: 25,
                    initialValue: _editable,
                    enableMouseWheel: _wheel,
                    announceChanges: _announce,
                    onChanged: (int v) => setState(() => _editable = v),
                  ),
                  const SizedBox(width: 16),
                  Text('Pages: $_editable'),
                ],
              ),
            ),
            _Section(
              title: 'Custom style, compact',
              caption:
                  'QuantityStepperTheme changes the colours and the painted '
                  'size. Every button is still a 48x48 target.',
              child: Wrap(
                spacing: 24,
                runSpacing: 16,
                children: const <Widget>[
                  QuantityStepper(
                    label: 'Seats',
                    compact: true,
                    max: 12,
                    incrementIcon: Icons.add_circle_outline,
                    decrementIcon: Icons.remove_circle_outline,
                    style: QuantityStepperTheme(
                      buttonColor: Color(0xFF1B7F5C),
                      iconColor: Colors.white,
                      borderColor: Color(0xFF1B7F5C),
                    ),
                  ),
                  QuantityStepper(
                    label: 'Disabled example',
                    initialValue: 3,
                    enabled: false,
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class _Section extends StatelessWidget {
  const _Section({
    required this.title,
    required this.caption,
    required this.child,
  });

  final String title;
  final String caption;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    final TextTheme text = Theme.of(context).textTheme;
    return Padding(
      padding: const EdgeInsets.only(bottom: 28),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Semantics(header: true, child: Text(title, style: text.titleMedium)),
          const SizedBox(height: 4),
          Text(caption, style: text.bodySmall),
          const SizedBox(height: 12),
          child,
        ],
      ),
    );
  }
}
0
likes
160
points
--
downloads
screenshot

Documentation

API reference

Publisher

verified publisherdaniotech.com

Accessible quantity stepper for Flutter: screen-reader adjust actions, 48dp targets, long-press repeat, keyboard support, a real FormField. No dependencies.

Homepage
Repository (GitHub)
View/report issues

Topics

#accessibility #input #form #widget #stepper

License

MIT (license)

Dependencies

flutter

More

Packages that depend on quantity_stepper