Screens / dashboard
Finance plan
Shows the total saved on a band in the theme color, savings goals in a carousel with their pace, monthly budgets as rings with overruns flagged, and adds money to a goal from a sheet.
Import
Finance plan is part of Fliin Pro. With a license key, sign in once with fliin login, then add it in a Flutter app where fliin init has run:
fliin login
fliin add finance-planAlso installs: app-bar, button, chart, chip, core, empty-state, icons, link-button, menu, page, progress, sheet, skeleton, surface, text-field
Examples
Ready
The savings plan of a finance app: the total saved and the plan's actions on the accent, then the goals in a carousel and the month's budgets as rings, under the app's floating tab bar. "Add money" opens a sheet that moves money from the account to the goal.
import 'package:your_app/ui/fliin/components/app_bar/fl_app_bar.dart';
import 'package:your_app/ui/fliin/components/tab_bar/fl_tab_bar.dart';
import 'package:your_app/ui/fliin/core/fliin.dart';
import 'package:your_app/ui/fliin/icons/fl_icons.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_controller.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_data.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_screen.dart';
import 'package:flutter/widgets.dart';
class FinancePlanReadyExample extends StatefulWidget {
const FinancePlanReadyExample({super.key});
@override
State<FinancePlanReadyExample> createState() => _FinancePlanReadyExampleState();
}
class _FinancePlanReadyExampleState extends State<FinancePlanReadyExample> {
int _tab = 2;
@override
Widget build(BuildContext context) {
return FinancePlanScreen.standalone(
service: const SamplePlanService(latency: Duration(milliseconds: 900)),
initial: PlanLoaded(samplePlanSnapshot()),
onMenu: () {},
actions: [FlAppBarAction(icon: FlIcon(FlIcons.bell), label: 'Notifications', onPressed: () {})],
onAddBudget: () {},
onSeeAllGoals: () {},
onDeleteGoal: (_) {},
now: samplePlanNow,
tabBar: FlBottomTabBar(
floating: true,
translucent: true,
labels: FlBottomTabLabels.beside,
indicator: FlBottomTabIndicator.capsule,
index: _tab,
onSelected: (index) => setState(() => _tab = index),
tabs: [
FlBottomTab(icon: FlIcon(FlIcons.house), label: 'Home'),
FlBottomTab(icon: FlIcon(FlIcons.chartColumn), label: 'Report'),
FlBottomTab(icon: FlIcon(FlIcons.chartDonut), label: 'Plan'),
FlBottomTab(icon: FlIcon(FlIcons.gear), label: 'Settings'),
],
),
);
}
}Loading
While the plan loads, skeletons keep the place of the total, a goal card and the budgets.
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_controller.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_data.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_screen.dart';
import 'package:flutter/widgets.dart';
class FinancePlanLoadingExample extends StatelessWidget {
const FinancePlanLoadingExample({super.key});
@override
Widget build(BuildContext context) {
return FinancePlanScreen.standalone(
service: const SamplePlanService(latency: Duration(milliseconds: 900)),
initial: const PlanLoading(),
now: samplePlanNow,
);
}
}Empty
No goal and no budget yet: the plan invites to set a first goal, or a budget.
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_controller.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_data.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_screen.dart';
import 'package:flutter/widgets.dart';
class FinancePlanEmptyExample extends StatelessWidget {
const FinancePlanEmptyExample({super.key});
@override
Widget build(BuildContext context) {
return FinancePlanScreen.standalone(
service: const SamplePlanService(latency: Duration(milliseconds: 900)),
initial: const PlanEmpty(),
now: samplePlanNow,
);
}
}Error
The plan could not load: the screen says so and offers to try again.
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_controller.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_data.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_screen.dart';
import 'package:flutter/widgets.dart';
class FinancePlanErrorExample extends StatelessWidget {
const FinancePlanErrorExample({super.key});
@override
Widget build(BuildContext context) {
return FinancePlanScreen.standalone(
service: const SamplePlanService(latency: Duration(milliseconds: 900)),
initial: const PlanFailed(),
now: samplePlanNow,
);
}
}Offline
Offline with the plan already loaded: the goals and budgets stay, a notice on the band dates them, and money cannot be added.
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_controller.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_data.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_screen.dart';
import 'package:flutter/widgets.dart';
class FinancePlanOfflineExample extends StatelessWidget {
const FinancePlanOfflineExample({super.key});
@override
Widget build(BuildContext context) {
return FinancePlanScreen.standalone(
service: const SamplePlanService(latency: Duration(milliseconds: 900)),
initial: PlanLoaded(samplePlanSnapshot(), notice: PlanNotice.offline),
now: samplePlanNow,
);
}
}Offline empty
Offline with nothing loaded yet: the screen asks to reconnect.
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_controller.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_data.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_screen.dart';
import 'package:flutter/widgets.dart';
class FinancePlanOfflineEmptyExample extends StatelessWidget {
const FinancePlanOfflineEmptyExample({super.key});
@override
Widget build(BuildContext context) {
return FinancePlanScreen.standalone(
service: const SamplePlanService(latency: Duration(milliseconds: 900)),
initial: const PlanOffline(),
now: samplePlanNow,
);
}
}Routing
fliin add finance-plan prints the route for the router it finds in your pubspec, a file next to the page that wires its navigation. Each of them:
go_router
// lib/finance_plan_route.dart, next to finance_plan_page.dart.
import 'package:go_router/go_router.dart';
import 'finance_plan_page.dart';
/// Add financePlanRoute to the routes of your GoRouter.
final financePlanRoute = GoRoute(
path: '/plan',
builder: (context, state) => FinancePlanPage(
// Alone in the stack (deep link, go): your app's home.
onBack: () => context.canPop() ? context.pop() : context.go('/'),
),
);navigator
// lib/finance_plan_route.dart, next to finance_plan_page.dart.
import 'package:flutter/material.dart';
import 'finance_plan_page.dart';
/// Opens “Finance plan”: `Navigator.of(context).push(financePlanRoute())`.
Route<void> financePlanRoute() => MaterialPageRoute<void>(
builder: (context) => FinancePlanPage(
onBack: () => Navigator.of(context).maybePop(),
),
);State
The controller is a ChangeNotifier from widgets.dart, with no package. fliin add finance-plan prints the page that holds it with the state management it finds, its imports written for your package; below they read your_app. Your backend implements the Service interface of the screen's _data.dart file:
none
// lib/finance_plan_page.dart
// Your backend: implement PlanService (finance_plan_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_data.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_screen.dart';
import 'package:flutter/widgets.dart';
class FinancePlanPage extends StatelessWidget {
const FinancePlanPage({
super.key,
required this.onBack,
});
final VoidCallback onBack;
@override
Widget build(BuildContext context) => FinancePlanScreen.standalone(
service: const SamplePlanService(),
onBack: onBack,
);
}provider
// lib/finance_plan_page.dart
// Your backend: implement PlanService (finance_plan_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_controller.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_data.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
class FinancePlanPage extends StatelessWidget {
const FinancePlanPage({
super.key,
required this.onBack,
});
final VoidCallback onBack;
@override
Widget build(BuildContext context) => ChangeNotifierProvider(
create: (_) => FinancePlanController(service: const SamplePlanService())..load(),
child: Builder(
builder: (context) => FinancePlanScreen(
controller: context.read<FinancePlanController>(),
onBack: onBack,
),
),
);
}riverpod
// lib/finance_plan_page.dart
// Your backend: implement PlanService (finance_plan_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_controller.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_data.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final financePlanProvider = Provider.autoDispose<FinancePlanController>((ref) {
final controller = FinancePlanController(service: const SamplePlanService())..load();
ref.onDispose(controller.dispose);
return controller;
});
class FinancePlanPage extends ConsumerWidget {
const FinancePlanPage({
super.key,
required this.onBack,
});
final VoidCallback onBack;
@override
Widget build(BuildContext context, WidgetRef ref) => FinancePlanScreen(
controller: ref.watch(financePlanProvider),
onBack: onBack,
);
}bloc
// lib/finance_plan_page.dart
// Your backend: implement PlanService (finance_plan_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_controller.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_data.dart';
import 'package:your_app/ui/fliin/screens/finance_plan/finance_plan_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
/// The screen's controller as a bloc, for your BlocListener and BlocBuilder:
/// one emission per controller notification. `version` tells them apart
/// when `state` does not change (mode, filters, text…, read them on
/// `controller`). The screen listens to the controller itself.
class FinancePlanCubit extends Cubit<({PlanState state, int version})> {
FinancePlanCubit(this.controller) : super((state: controller.state, version: 0)) {
controller.addListener(_changed);
}
final FinancePlanController controller;
void _changed() => emit((state: controller.state, version: state.version + 1));
@override
Future<void> close() {
controller
..removeListener(_changed)
..dispose();
return super.close();
}
}
class FinancePlanPage extends StatelessWidget {
const FinancePlanPage({
super.key,
required this.onBack,
});
final VoidCallback onBack;
@override
Widget build(BuildContext context) => BlocProvider(
create: (_) => FinancePlanCubit(FinancePlanController(service: const SamplePlanService())..load()),
child: Builder(
builder: (context) => FinancePlanScreen(
controller: context.read<FinancePlanCubit>().controller,
onBack: onBack,
),
),
);
}API
FinancePlanScreen: class extends StatefulWidget
FinancePlanScreen.new: const new({super.key, required FinancePlanController controller, VoidCallback? onBack, VoidCallback? onMenu, List<FlAppBarAction> actions, ValueChanged<PlanGoal>? onEditGoal, ValueChanged<PlanGoal>? onDeleteGoal, VoidCallback? onAddGoal, VoidCallback? onAddBudget, VoidCallback? onSeeAllGoals, Widget? tabBar, PlanFormat? format, DateTime? now})
FinancePlanScreen.standalone: const new({super.key, required PlanService service, PlanState? initial, VoidCallback? onBack, VoidCallback? onMenu, List<FlAppBarAction> actions, ValueChanged<PlanGoal>? onEditGoal, ValueChanged<PlanGoal>? onDeleteGoal, VoidCallback? onAddGoal, VoidCallback? onAddBudget, VoidCallback? onSeeAllGoals, Widget? tabBar, PlanFormat? format, DateTime? now})
FinancePlanScreen.controller: get FinancePlanController?
FinancePlanScreen.service: get PlanService?
FinancePlanScreen.initial: get PlanState?
FinancePlanScreen.onBack: get VoidCallback?
FinancePlanScreen.onMenu: get VoidCallback?
FinancePlanScreen.actions: get List<FlAppBarAction>
FinancePlanScreen.onEditGoal: get ValueChanged<PlanGoal>?
FinancePlanScreen.onDeleteGoal: get ValueChanged<PlanGoal>?
FinancePlanScreen.onAddGoal: get VoidCallback?
FinancePlanScreen.onAddBudget: get VoidCallback?
FinancePlanScreen.onSeeAllGoals: get VoidCallback?
FinancePlanScreen.tabBar: get Widget?
FinancePlanScreen.format: get PlanFormat?
FinancePlanScreen.now: get DateTime?
FinancePlanScreen.createState: State<FinancePlanScreen>()
PlanState: sealed class permits {PlanEmpty, PlanFailed, PlanLoaded, PlanLoading, PlanOffline}
PlanState.new: const new()
PlanLoading: final class extends PlanState
PlanLoading.new: const new()
PlanNotice: enum {refreshFailed, offline}
PlanNotice.refreshFailed: static final PlanNotice
PlanNotice.offline: static final PlanNotice
PlanLoaded: final class extends PlanState
PlanLoaded.new: const new(PlanSnapshot snapshot, {PlanNotice? notice})
PlanLoaded.snapshot: get PlanSnapshot
PlanLoaded.notice: get PlanNotice?
PlanEmpty: final class extends PlanState
PlanEmpty.new: const new({PlanNotice? notice})
PlanEmpty.notice: get PlanNotice?
PlanFailed: final class extends PlanState
PlanFailed.new: const new()
PlanOffline: final class extends PlanState
PlanOffline.new: const new()
PlanAddResult: enum {added, offline, failed}
PlanAddResult.added: static final PlanAddResult
PlanAddResult.offline: static final PlanAddResult
PlanAddResult.failed: static final PlanAddResult
PlanSaveResult: enum {saved, offline, failed}
PlanSaveResult.saved: static final PlanSaveResult
PlanSaveResult.offline: static final PlanSaveResult
PlanSaveResult.failed: static final PlanSaveResult
FinancePlanController: class extends ChangeNotifier
FinancePlanController.new: new({required PlanService service, PlanState initial})
FinancePlanController.service: get PlanService
FinancePlanController.state: get PlanState
FinancePlanController.load: Future<void>()
FinancePlanController.refresh: Future<bool>()
FinancePlanController.addMoney: Future<PlanAddResult>(String goalId, int cents)
FinancePlanController.saveGoal: Future<PlanSaveResult>(PlanGoalDraft draft)
FinancePlanController.dispose: void()
PlanGoal: final class
PlanGoal.new: const new({required String id, required String name, required int saved, required int target, required DateTime due, required double expected, FlIconData? icon})
PlanGoal.id: get String
PlanGoal.name: get String
PlanGoal.saved: get int
PlanGoal.target: get int
PlanGoal.due: get DateTime
PlanGoal.expected: get double
PlanGoal.icon: get FlIconData?
PlanGoal.progress: get double
PlanGoal.left: get int
PlanGoal.behind: get double
PlanGoal.adding: PlanGoal(int cents)
PlanGoalDraft: final class
PlanGoalDraft.new: const new({String? id, required String name, required int target, required DateTime due, FlIconData? icon})
PlanGoalDraft.id: get String?
PlanGoalDraft.name: get String
PlanGoalDraft.target: get int
PlanGoalDraft.due: get DateTime
PlanGoalDraft.icon: get FlIconData?
PlanBudget: final class
PlanBudget.new: const new({required String id, required String name, required int spent, required int limit, FlIconData? icon})
PlanBudget.id: get String
PlanBudget.name: get String
PlanBudget.spent: get int
PlanBudget.limit: get int
PlanBudget.icon: get FlIconData?
PlanBudget.share: get double
PlanBudget.over: get bool
PlanAccount: final class
PlanAccount.new: const new({required String name, required int available})
PlanAccount.name: get String
PlanAccount.available: get int
PlanSnapshot: final class
PlanSnapshot.new: const new({required List<PlanGoal> goals, required List<PlanBudget> budgets, required DateTime month, required int daysLeft, required DateTime updatedAt, PlanAccount? account, int? addedThisMonth})
PlanSnapshot.goals: get List<PlanGoal>
PlanSnapshot.budgets: get List<PlanBudget>
PlanSnapshot.month: get DateTime
PlanSnapshot.daysLeft: get int
PlanSnapshot.updatedAt: get DateTime
PlanSnapshot.account: get PlanAccount?
PlanSnapshot.addedThisMonth: get int?
PlanSnapshot.isEmpty: get bool
PlanSnapshot.saved: get int
PlanSnapshot.adding: PlanSnapshot(String goalId, int cents)
PlanSnapshot.saving: PlanSnapshot(PlanGoal goal)
PlanNoNetwork: final class implements Exception
PlanNoNetwork.new: const new({PlanSnapshot? lastKnown})
PlanNoNetwork.lastKnown: get PlanSnapshot?
PlanService: abstract interface class
PlanService.new: new()
PlanService.load: abstract Future<PlanSnapshot>()
PlanService.addMoney: abstract Future<void>(String goalId, int cents)
PlanService.saveGoal: abstract Future<PlanGoal>(PlanGoalDraft draft)
PlanFormat: abstract interface class
PlanFormat.new: new()
PlanFormat.amount: abstract String(int cents)
PlanFormat.whole: abstract String(int cents)
PlanFormat.currency: abstract get String
PlanFormat.currencyFirst: abstract get bool
SamplePlanFormat: final class implements PlanFormat
SamplePlanFormat.new: const new(String language)
SamplePlanFormat.language: get String
SamplePlanFormat.amount: String(int cents)
SamplePlanFormat.whole: String(int cents)
SamplePlanFormat.currency: get String
SamplePlanFormat.currencyFirst: get bool
samplePlanNow: get dynamic
samplePlanGoals: get dynamic
samplePlanBudgets: get dynamic
samplePlanSnapshot: PlanSnapshot()
sampleEmptyPlan: PlanSnapshot()
SamplePlanService: final class implements PlanService
SamplePlanService.new: const new({PlanSnapshot Function()? snapshot, Duration latency})
SamplePlanService.snapshot: get PlanSnapshot Function()?
SamplePlanService.latency: get Duration
SamplePlanService.load: Future<PlanSnapshot>()
SamplePlanService.addMoney: Future<void>(String goalId, int cents)
SamplePlanService.saveGoal: Future<PlanGoal>(PlanGoalDraft draft)
financePlanL10n: const Map<String, Map<String, String>>
FinancePlanStrings: final class
FinancePlanStrings.new: const new(String language)
FinancePlanStrings.of: factory new(BuildContext context)
FinancePlanStrings.language: get String
FinancePlanStrings.title: get String
FinancePlanStrings.back: get String
FinancePlanStrings.refresh: get String
FinancePlanStrings.loading: get String
FinancePlanStrings.menu: get String
FinancePlanStrings.savedAcross: String(int goals)
FinancePlanStrings.totalSaved: get String
FinancePlanStrings.thisMonth: String(String amount)
FinancePlanStrings.actions: get String
FinancePlanStrings.addBudget: get String
FinancePlanStrings.seeAll: get String
FinancePlanStrings.goals: get String
FinancePlanStrings.goalsList: get String
FinancePlanStrings.month: String(DateTime at)
FinancePlanStrings.date: String(DateTime at)
FinancePlanStrings.due: String(DateTime at)
FinancePlanStrings.ofTarget: String(String target)
FinancePlanStrings.toGo: String(String? amount)
FinancePlanStrings.expected: String(String percent)
FinancePlanStrings.status: String(double behind)
FinancePlanStrings.goalReading: String(String name, String saved, String target, String percent, String expected, String status)
FinancePlanStrings.goalOptions: String(String name)
FinancePlanStrings.addMoney: get String
FinancePlanStrings.editGoal: get String
FinancePlanStrings.deleteGoal: get String
FinancePlanStrings.budgetsLeft: String(int daysLeft)
FinancePlanStrings.spent: String(String spent, String limit)
FinancePlanStrings.over: String(String amount)
FinancePlanStrings.budgetReading: String(String name, String spent, String limit, String percent, {required bool over})
FinancePlanStrings.addGoal: get String
FinancePlanStrings.newGoal: get String
FinancePlanStrings.goalName: get String
FinancePlanStrings.goalNameHint: get String
FinancePlanStrings.goalTarget: get String
FinancePlanStrings.goalDue: get String
FinancePlanStrings.monthYear: String(DateTime at)
FinancePlanStrings.earlier: get String
FinancePlanStrings.later: get String
FinancePlanStrings.goalIcon: get String
FinancePlanStrings.goalIconName: String(int index)
FinancePlanStrings.createGoal: get String
FinancePlanStrings.saveGoal: get String
FinancePlanStrings.saveFailed: get String
FinancePlanStrings.saveOffline: get String
FinancePlanStrings.to: String(String goal)
FinancePlanStrings.amount: get String
FinancePlanStrings.plus: String(String amount)
FinancePlanStrings.from: String(String account, String available)
FinancePlanStrings.tooMuch: String(String available)
FinancePlanStrings.addAmount: String(String amount)
FinancePlanStrings.addFailed: get String
FinancePlanStrings.addOffline: get String
FinancePlanStrings.close: get String
FinancePlanStrings.percent: String(double share)
FinancePlanStrings.emptyTitle: get String
FinancePlanStrings.emptyMessage: get String
FinancePlanStrings.errorTitle: get String
FinancePlanStrings.errorMessage: get String
FinancePlanStrings.offlineTitle: get String
FinancePlanStrings.offlineMessage: get String
FinancePlanStrings.retry: get String
FinancePlanStrings.refreshFailed: get String
FinancePlanStrings.time: String(DateTime at)
FinancePlanStrings.offlineSince: String(DateTime at, DateTime now)Strings
Every label in English, French and Arabic. The screen follows the closest Localizations; the same table is in the manifest, ready for your ARB files.
| Key | en | fr | ar |
|---|---|---|---|
financePlanTitle | Plan | Plan | الخطة |
financePlanBack | Back | Retour | رجوع |
financePlanMenu | Menu | Menu | القائمة |
financePlanRefresh | Refresh | Actualiser | تحديث |
financePlanLoading | Loading your plan | Chargement de votre plan | جارٍ تحميل خطتك |
financePlanSavedAcross | Saved across {count} goals | Épargné sur {count} objectifs | المدّخر في الأهداف: {count} |
financePlanSavedOne | Saved in 1 goal | Épargné sur 1 objectif | المدّخر في هدف واحد |
financePlanTotalSaved | Total saved | Total épargné | إجمالي المدّخر |
financePlanThisMonth | {amount} this month | {amount} ce mois-ci | {amount} هذا الشهر |
financePlanActions | Plan actions | Actions du plan | إجراءات الخطة |
financePlanAddBudget | Add budget | Ajouter un budget | إضافة ميزانية |
financePlanGoals | Goals | Objectifs | الأهداف |
financePlanSeeAll | See all | Tout voir | عرض الكل |
financePlanGoalsList | Savings goals | Objectifs d’épargne | أهداف الادخار |
financePlanMonths | January,February,March,April,May,June,July,August,September,October,November,December | janvier,février,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre | يناير,فبراير,مارس,أبريل,مايو,يونيو,يوليو,أغسطس,سبتمبر,أكتوبر,نوفمبر,ديسمبر |
financePlanDate | {day} {month} {year} | {day} {month} {year} | {day} {month} {year} |
financePlanDue | Due {date} | Échéance le {date} | الموعد {date} |
financePlanOfTarget | of {target} | sur {target} | من {target} |
financePlanToGo | {amount} to go | Encore {amount} | يتبقى {amount} |
financePlanReached | Target reached | Objectif atteint | تحقق الهدف |
financePlanExpected | Expected {percent} | Prévu {percent} | المتوقع {percent} |
financePlanOnTrack | On track | Dans les temps | في الموعد |
financePlanBehind | {percent} behind schedule | {percent} de retard | متأخر بنسبة {percent} |
financePlanGoalReading | {name}, {saved} of {target}, {percent}. Expected {expected} by now. {status}. | {name}, {saved} sur {target}, {percent}. {expected} prévus à ce jour. {status}. | {name}، {saved} من {target}، {percent}. المتوقع حتى الآن {expected}. {status}. |
financePlanGoalOptions | Options for {name} | Options de {name} | خيارات {name} |
financePlanAddMoney | Add money | Ajouter de l’argent | إضافة مال |
financePlanEditGoal | Edit goal | Modifier l’objectif | تعديل الهدف |
financePlanDeleteGoal | Delete goal | Supprimer l’objectif | حذف الهدف |
financePlanBudgetsLeft | Budgets · {left} | Budgets · {left} | الميزانيات · {left} |
financePlanDaysLeft | {count} days left | {count} jours restants | تبقّى {count} يومًا |
financePlanDayLeft | 1 day left | 1 jour restant | تبقّى يوم واحد |
financePlanLastDay | Last day | Dernier jour | اليوم الأخير |
financePlanSpent | {spent} of {limit} | {spent} sur {limit} | {spent} من {limit} |
financePlanOver | {amount} over | {amount} de trop | تجاوز {amount} |
financePlanBudgetReading | {name} budget, {spent} of {limit}, {percent} | Budget {name}, {spent} sur {limit}, {percent} | ميزانية {name}، {spent} من {limit}، {percent} |
financePlanBudgetReadingOver | {name} budget, {spent} of {limit}, {percent}, over budget | Budget {name}, {spent} sur {limit}, {percent}, dépassé | ميزانية {name}، {spent} من {limit}، {percent}، تم تجاوزها |
financePlanAddGoal | Add goal | Ajouter un objectif | إضافة هدف |
financePlanNewGoal | New goal | Nouvel objectif | هدف جديد |
financePlanGoalName | Name | Nom | الاسم |
financePlanGoalNameHint | Trip to Japan | Voyage au Japon | رحلة إلى اليابان |
financePlanGoalTarget | Target | Montant visé | المبلغ المستهدف |
financePlanGoalDue | Reach it by | À atteindre en | الموعد |
financePlanMonthYear | {month} {year} | {month} {year} | {month} {year} |
financePlanEarlier | One month earlier | Un mois plus tôt | قبل شهر |
financePlanLater | One month later | Un mois plus tard | بعد شهر |
financePlanGoalIcon | Icon | Icône | الأيقونة |
financePlanGoalIcons | Safety,Travel,Tech,Home,Car,Health,Reward,Savings | Sécurité,Voyage,Tech,Maison,Voiture,Santé,Récompense,Épargne | الأمان,السفر,التقنية,المنزل,السيارة,الصحة,المكافأة,الادخار |
financePlanCreateGoal | Create goal | Créer l’objectif | إنشاء الهدف |
financePlanSaveGoal | Save | Enregistrer | حفظ |
financePlanSaveFailed | Couldn’t save the goal. Try again. | Impossible d’enregistrer l’objectif. Réessayez. | تعذّر حفظ الهدف. أعد المحاولة. |
financePlanSaveOffline | You’re offline. Nothing was saved. | Vous êtes hors ligne. Rien n’a été enregistré. | أنت غير متصل. لم يُحفظ شيء. |
financePlanTo | To {goal} | Vers {goal} | إلى {goal} |
financePlanAmount | Amount | Montant | المبلغ |
financePlanPlus | + {amount} | + {amount} | + {amount} |
financePlanFrom | From {account} · {available} available | Depuis {account} · {available} disponibles | من {account} · {available} متاح |
financePlanTooMuch | More than the {available} available | Plus que les {available} disponibles | أكثر من {available} المتاح |
financePlanAddAmount | Add {amount} | Ajouter {amount} | إضافة {amount} |
financePlanAddFailed | Couldn’t add the money. Try again. | Impossible d’ajouter l’argent. Réessayez. | تعذّرت إضافة المال. أعد المحاولة. |
financePlanAddOffline | You’re offline. Nothing was moved. | Vous êtes hors ligne. Rien n’a été déplacé. | أنت غير متصل. لم يُنقل أي مبلغ. |
financePlanClose | Close | Fermer | إغلاق |
financePlanEmptyTitle | Start with a goal | Commencez par un objectif | ابدأ بهدف |
financePlanEmptyMessage | Set money aside for what matters, and keep your monthly budgets here. | Mettez de l’argent de côté pour ce qui compte, et gardez ici vos budgets du mois. | ادّخر لما يهمّك، واحتفظ بميزانياتك الشهرية هنا. |
financePlanErrorTitle | We couldn’t load your plan | Impossible de charger votre plan | تعذّر تحميل خطتك |
financePlanErrorMessage | Our server didn’t answer. Your money is safe. | Notre serveur n’a pas répondu. Votre argent ne risque rien. | لم يستجب خادمنا. أموالك في أمان. |
financePlanOfflineTitle | You are offline | Vous êtes hors ligne | أنت غير متصل |
financePlanOfflineMessage | Check your connection, then try again. | Vérifiez votre connexion, puis réessayez. | تحقق من اتصالك ثم أعد المحاولة. |
financePlanRetry | Try again | Réessayer | أعد المحاولة |
financePlanRefreshFailed | Couldn’t refresh. Amounts may be out of date. | Actualisation impossible. Les montants ne sont peut-être plus à jour. | تعذّر التحديث. قد لا تكون المبالغ محدّثة. |
financePlanOfflineSince | You’re offline. Updated {date} at {time}. | Vous êtes hors ligne. Mis à jour le {date} à {time}. | أنت غير متصل. آخر تحديث {date} الساعة {time}. |
financePlanOfflineToday | You’re offline. Updated today at {time}. | Vous êtes hors ligne. Mis à jour aujourd’hui à {time}. | أنت غير متصل. آخر تحديث اليوم الساعة {time}. |
Reference renders
The 16 images published with version 0.2.0, rendered by the shared harness on macOS with Flutter 3.47.7. Each shows every state side by side, at the size of an iPhone (390 × 844) or of an Android phone (412 × 915). fliin verify finance-plan compares your copy to them.
















Use it to
- show savings goals and whether they are on schedule
- add money to a goal from a bank account
- show monthly budgets by category and which ones are over
- create a savings goal with its target, due month and icon
- offer to set a first savings goal when there is none
- keep the last known plan offline, with its date
- Not for
- the balance and the latest transactions: use the finance dashboard (finance-dashboard)
- a single progress bar: use a progress indicator (progress)
- States