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,
),
],
),
)
],
),
);
}
}