FlutterDart

Flutter Package: count_number

Animiert den Zählvorgang eines Wertes auf der Grundlage einer Spring Simulation. Startet einen internen periodischen Timer, der bei jedem Tick den Zustand der Simulation überprüft und ggf. einen der Callbacks aufruft. Der Wert kann vom Typ Integer oder Double sein.

Beim Arbeiten an einem kleinen Hobby-Projekt wollte ich einen animierten Zählvorgang implementieren. Dieser sollte je näher er dem Zielwert kommt immer langsamer hoch- bzw. runterzählen. Aus dem Code entstand dann dieses Package.

Features

  • Zählt animiert eine Zahl auf oder ab
  • Unterstützt Integer- und Double-Werte
  • Ruft die Callbacks nur dann auf, wenn sie wirklich nötig sind
  • Kann als dynamisch initialisiert werden
    • Das Setzen des Eigenschaftswertes löst einen neuen Zählvorgang aus

Erste Schritte

Füge die Abhängigkeit hinzu:

flutter pub add count_number

Importiere das Package:

import 'package:count_number/count_number.dart';

Verwendung

Initialisieren eines CountNumber-Objekts:

class _HomeState extends State<Home> {
  int _number = 0;
  late CountNumber _countNumber;

  @override
  void initState() {
    _countNumber = CountNumber(
      endValue: 50,
      onUpdate: (value) => setState(() => _number = value as int),
    );
    super.initState();
  }

  @override
  void dispose() {
    _countNumber.stop();
    super.dispose();
  }
}

Ausführen der Methode start():

@override
  Widget build(BuildContext context) {
    _countNumber.start();

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Expanded(
        child: Center(
            child: Text(
            _number.toString(),
            style: Theme.of(context).textTheme.headline1,
          ),
        )
      ),
    );
  }

Beispiel

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

void main() {
  runApp(const CountApp());
}

class CountApp extends StatelessWidget {
  const CountApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Count Numbers',
      home: const Home('Count Numbers'),
      theme: Theme.of(context).copyWith(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
      ),
      color: Colors.lightGreen,
    );
  }
}

class Home extends StatefulWidget {
  final String title;

  const Home(this.title, {super.key});

  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  num _number = 0;
  bool _isNumActive = true;
  late CountNumber _countNumber;
  late TextEditingController _txtController;

  void _onPressed() {
    if (_isNumActive) {
      num? newVal = num.tryParse(_txtController.text);
      if (newVal != null) {
        _isNumActive = false;
        _countNumber.value = newVal;
      }
    }
  }

  void onCountUpdate(num value) {
    setState(() => _number = value);
  }

  void onCountDone() {
    setState(() => _isNumActive = true);
  }

  @override
  void initState() {
    _txtController = TextEditingController(text: '0');
    _countNumber = CountNumber(
      endValue: _number,
      onUpdate: onCountUpdate,
      onDone: onCountDone,
      isDynamic: true,
    );
    super.initState();
  }

  @override
  void dispose() {
    _countNumber.stop();
    _txtController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Column(
        children: [
          Expanded(
              child: Center(
                child: Text(
                  CountNumber.isInteger(_number)
                      ? _number.toString()
                      : _number.toStringAsFixed(2),
                  style: Theme.of(context).textTheme.headline1,
                ),
              )),
          Card(
            elevation: 12,
            margin: const EdgeInsets.symmetric(
              vertical: 30,
              horizontal: 20,
            ),
            child: Row(
              children: [
                const SizedBox(
                  width: 15,
                ),
                Expanded(
                  flex: 2,
                  child: Padding(
                    padding: const EdgeInsets.symmetric(
                      vertical: 5,
                      horizontal: 20,
                    ),
                    child: TextField(
                      controller: _txtController,
                      enabled: _isNumActive,
                      keyboardType: TextInputType.number,
                      textAlign: TextAlign.center,
                      onSubmitted: (_) => _onPressed(),
                      style: Theme.of(context).textTheme.headline4?.copyWith(
                        fontWeight: FontWeight.w200,
                      ),
                      decoration: const InputDecoration(
                        fillColor: Colors.white70,
                        filled: true,
                        contentPadding: EdgeInsets.all(10),
                        border: OutlineInputBorder(
                            borderRadius: BorderRadius.all(Radius.circular(10)),
                            borderSide: BorderSide(
                              color: Colors.cyan,
                            )),
                      ),
                    ),
                  ),
                ),
                const Spacer(),
                TextButton(
                  onPressed: _onPressed,
                  child: const Text('Count!'),
                ),
                const SizedBox(
                  width: 15,
                ),
              ],
            ),
          )
        ],
      ),
    );
  }
}

Package

count_number | Flutter Package
count_number - Counts up or down a value based on a spring animation.
image

Repository

GitHub - jibbex/count_number: Counts up or down a value based on a spring animation.
Counts up or down a value based on a spring animation. - GitHub - jibbex/count_number: Counts up or down a value based on a spring animation.
image