شمارندهٔ متصل به متغیر
این نمونه با دکمههای افزایش و کاهش، مقدار یک متغیر اپ را تغییر میدهد. مقدار با watchVar خوانده میشود؛ اگر عملکرد دیگری در اپ همان متغیر را تغییر دهد، شمارنده هم مقدار جدید را نشان میدهد.
افزودن نمونه
- ویجت Flutter سفارشی را اضافه کنید.
- فایل counter.dart را دانلود کنید و در فرم بارگذاری کنید.
- عرض را «به اندازه والد» و ارتفاع را «به اندازه محتوا» قرار دهید.
- پارامترهای زیر را وارد کنید و بعد از ذخیرهٔ صفحه، خروجی جدید بسازید.
{
"title": "تعداد سفارشها",
"variable": "order_count",
"initial": 0,
"step": 1,
"storeOnDevice": true
}
پارامترها
| کلید | نوع و پیشفرض | کاربرد |
|---|---|---|
title | رشته؛ «شمارنده» | عنوان کارت |
variable | رشته؛ custom_counter | نام متغیر مشترک با اپ |
initial | عدد؛ 0 | مقدار هنگام ساخت متغیر برای اولین بار |
step | عدد؛ 1 | میزان تغییر با هر لمس؛ برای افزایش معمولی عدد مثبت بدهید |
storeOnDevice | بولی؛ false | حفظ مقدار روی همان دستگاه بین اجراهای اپ |
نام ساده بدون نقطه و اندیس انتخاب کنید. مقدار initial متغیر موجود را بازنویسی نمیکند. تغییر storeOnDevice دادهٔ قبلی را منتقل نمیکند؛ برای نوع ذخیرهٔ متفاوت، نام متغیر تازه بدهید.
ارتباط با پنل
- برای نمایش همان عدد در جای دیگری، نمایشگر متغیر را به
order_countمتصل کنید. - در فهرست عملکردهای شمارنده، شناسهٔ اختیاری
onChangedتعریف کنید. پس از تغییر مقدار با دکمههای نمونه اجرا میشود. - تغییر متغیر توسط بخش دیگری از اپ، رابط را بهروز میکند؛ اما
onChangedاین نمونه را فراخوانی نمیکند. - دو شمارنده با
variableیکسان، مقدار مشترک دارند. برای استقلال نام متفاوت بدهید.
امتحان در خروجی
- با پارامترهای بالا، متغیر تازه از صفر آغاز میشود.
- سه بار افزایش دهید؛ مقدار باید ۳ شود.
- از یک عملکرد پنل، مقدار متغیر را به ۱۰ تغییر دهید؛ شمارنده هم ۱۰ را نشان میدهد.
- اپ را ببندید و دوباره باز کنید؛ با ذخیره روی دستگاه مقدار باید باقی بماند.
stepرا به ۵ تغییر دهید؛ لمس بعدی عدد را پنج واحد تغییر میدهد.
این نمونه حداقل و حداکثر ندارد؛ کاهش میتواند مقدار را منفی کند. مقدار شمارنده روی دستگاه ذخیره میشود و بین دستگاههای کاربر همگام نمیشود.
کد کامل
Stream در فیلد نگهداری شده و هنگام تغییر نام متغیر، اتصال در didUpdateWidget بهروز میشود:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:my_app/appeditor/custom/AppEditorWidgetContext.dart';
Widget buildCustomWidget(BuildContext context, AppEditorWidgetContext app) {
return CounterWidget(app: app);
}
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key, required this.app});
final AppEditorWidgetContext app;
State<CounterWidget> createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
late String _variable;
late Stream<dynamic> _values;
bool _busy = false;
double _number(dynamic value, double fallback) {
final result = value is num ? value.toDouble() : double.tryParse('$value');
return result != null && result.isFinite ? result : fallback;
}
void _bind() {
_variable = widget.app.params['variable'] as String? ?? 'custom_counter';
_values = widget.app.watchVar(_variable);
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) unawaited(_prepare());
});
}
Future<void> _prepare() async {
try {
await widget.app.addVar(
name: _variable,
value: _number(widget.app.params['initial'], 0),
storeOnDevice: widget.app.params['storeOnDevice'] == true,
);
} catch (error) {
widget.app.showMessage('ایجاد شمارنده ممکن نشد: $error');
}
}
void initState() {
super.initState();
_bind();
}
void didUpdateWidget(covariant CounterWidget oldWidget) {
super.didUpdateWidget(oldWidget);
final name = widget.app.params['variable'] as String? ?? 'custom_counter';
if (name != _variable || !identical(widget.app, oldWidget.app)) _bind();
}
Future<void> _change(double direction) async {
setState(() => _busy = true);
final app = widget.app;
final variable = _variable;
try {
await app.addVar(
name: variable,
value: _number(app.params['initial'], 0),
storeOnDevice: app.params['storeOnDevice'] == true,
);
if (!mounted) return;
final current = _number(app.getVar(variable), 0);
final step = _number(app.params['step'], 1);
await app.setVar(variable, current + step * direction);
if (mounted && app.hasAction('onChanged'))
await app.runAction('onChanged');
} catch (error) {
app.showMessage('تغییر شمارنده ممکن نشد: $error');
} finally {
if (mounted) setState(() => _busy = false);
}
}
Widget build(BuildContext context) {
return StreamBuilder<dynamic>(
stream: _values,
builder: (context, snapshot) {
final value = _number(
snapshot.data,
_number(widget.app.params['initial'], 0),
);
final text = value == value.truncateToDouble()
? value.toStringAsFixed(0)
: '$value';
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
widget.app.params['title'] as String? ?? 'شمارنده',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 12),
Text(text, style: Theme.of(context).textTheme.headlineLarge),
const SizedBox(height: 12),
Wrap(
spacing: 16,
children: [
OutlinedButton(
onPressed: _busy ? null : () => _change(-1),
child: const Text('−'),
),
FilledButton(
onPressed: _busy ? null : () => _change(1),
child: const Text('+'),
),
],
),
],
),
),
);
},
);
}
}