Kasir Grosir: starter Flutter POS (scaffold awal)

This commit is contained in:
onie
2026-10-17 13:10:06 +07:00
commit 636579a0d2
146 changed files with 5373 additions and 0 deletions

66
lib/main.dart Normal file
View File

@@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
import 'widgets/app_drawer.dart';
import 'widgets/template_screen.dart';
import 'screens/dashboard.dart';
import 'screens/pos.dart';
import 'screens/products.dart';
import 'screens/customers.dart';
import 'screens/suppliers.dart';
import 'screens/purchase_orders.dart';
import 'screens/accounts_receivable.dart';
import 'screens/inventory.dart';
import 'screens/reports.dart';
import 'screens/settings.dart';
import 'screens/users.dart';
import 'screens/utilities.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Kasir Grosir - Starter',
theme: ThemeData(
primarySwatch: Colors.indigo,
),
home: const HomePage(),
routes: {
'/dashboard': (_) => const DashboardScreen(),
'/pos': (_) => const PosScreen(),
'/products': (_) => const ProductsScreen(),
'/customers': (_) => const CustomersScreen(),
'/suppliers': (_) => const SuppliersScreen(),
'/purchase_orders': (_) => const PurchaseOrdersScreen(),
'/ar': (_) => const AccountsReceivableScreen(),
'/inventory': (_) => const InventoryScreen(),
'/reports': (_) => const ReportsScreen(),
'/settings': (_) => const SettingsScreen(),
'/users': (_) => const UsersScreen(),
'/utilities': (_) => const UtilitiesScreen(),
},
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return TemplateScreen(
title: 'Kasir Grosir - Home',
drawer: const AppDrawer(),
body: const Center(child: Text('Selamat datang di Kasir Grosir - pilih menu dari drawer')),
actions: const [
IconButton(onPressed: _noop, icon: Icon(Icons.search)),
],
);
}
static void _noop() {}
}

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import '../widgets/template_screen.dart';
import '../widgets/app_drawer.dart';
class AccountsReceivableScreen extends StatelessWidget {
const AccountsReceivableScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const TemplateScreen(
title: 'Accounts Receivable',
drawer: AppDrawer(),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Piutang (AR)', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
SizedBox(height: 12),
Text('TODO: Invoices, partial payments, aging report.'),
SizedBox(height: 12),
Text('Catatan:'),
Text('- Implement invoice model and payment entries.'),
],
),
);
}
}

View File

@@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import '../widgets/template_screen.dart';
import '../widgets/app_drawer.dart';
class CustomersScreen extends StatelessWidget {
const CustomersScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const TemplateScreen(
title: 'Customers',
drawer: AppDrawer(),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Pelanggan', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
SizedBox(height: 12),
Text('TODO: Customer list, price tier, credit limit, contact info.'),
SizedBox(height: 12),
Text('Catatan:'),
Text('- Simpan informasi credit limit & term_days untuk AR.'),
Text('- Integrate quick select from POS.'),
],
),
);
}
}

View File

@@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import '../widgets/template_screen.dart';
import '../widgets/app_drawer.dart';
class DashboardScreen extends StatelessWidget {
const DashboardScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const TemplateScreen(
title: 'Dashboard',
drawer: AppDrawer(),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Ringkasan Harian', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
SizedBox(height: 12),
Text('- Penjualan hari ini: TODO (implement)') ,
Text('- Piutang terutang: TODO (implement)'),
Text('- Stok kritis: TODO (implement)'),
SizedBox(height: 24),
Text('Catatan:', style: TextStyle(fontWeight: FontWeight.bold)),
Text('1. Ambil data penjualan lokal (SQLite) dan agregasi untuk ringkasan.'),
Text('2. Buat widget chart sederhana setelah data tersedia.'),
],
),
),
);
}
}

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import '../widgets/template_screen.dart';
import '../widgets/app_drawer.dart';
class InventoryScreen extends StatelessWidget {
const InventoryScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const TemplateScreen(
title: 'Inventory',
drawer: AppDrawer(),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Stok & Warehousing', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
SizedBox(height: 12),
Text('TODO: Multi-warehouse, stock adjustment, transfers.'),
SizedBox(height: 12),
Text('Catatan:'),
Text('- Keep stock movements log for audit.'),
],
),
);
}
}

51
lib/screens/pos.dart Normal file
View File

@@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import '../widgets/template_screen.dart';
import '../widgets/app_drawer.dart';
class PosScreen extends StatelessWidget {
const PosScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return TemplateScreen(
title: 'POS',
drawer: const AppDrawer(),
fab: FloatingActionButton(
onPressed: () {},
tooltip: 'New Transaction',
child: const Icon(Icons.add),
),
body: Column(
children: [
Card(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Row(
children: const [
Icon(Icons.search),
SizedBox(width: 8),
Expanded(child: TextField(decoration: InputDecoration.collapsed(hintText: 'Search products'))),
],
),
),
),
const SizedBox(height: 12),
Expanded(
child: GridView.builder(
itemCount: 12,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 0.9),
itemBuilder: (context, i) {
return Card(
child: InkWell(
onTap: () {},
child: Center(child: Text('Product ${i + 1}', textAlign: TextAlign.center)),
),
);
},
),
),
],
),
);
}
}

27
lib/screens/products.dart Normal file
View File

@@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import '../widgets/template_screen.dart';
import '../widgets/app_drawer.dart';
class ProductsScreen extends StatelessWidget {
const ProductsScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const TemplateScreen(
title: 'Products',
drawer: AppDrawer(),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Produk / Katalog', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
SizedBox(height: 12),
Text('TODO: Implement CRUD produk (SKU, barcode, harga, stok, unit, pack).'),
SizedBox(height: 12),
Text('Catatan:'),
Text('- Buat Room/SQLite entity untuk product.'),
Text('- Tambahkan import CSV untuk bulk upload.'),
],
),
);
}
}

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import '../widgets/template_screen.dart';
import '../widgets/app_drawer.dart';
class PurchaseOrdersScreen extends StatelessWidget {
const PurchaseOrdersScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const TemplateScreen(
title: 'Purchase Orders',
drawer: AppDrawer(),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Purchase Orders', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
SizedBox(height: 12),
Text('TODO: Create PO, receive partial/full, update stock & cost.'),
SizedBox(height: 12),
Text('Catatan:'),
Text('- Track expected_date & received_status.'),
],
),
);
}
}

26
lib/screens/reports.dart Normal file
View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import '../widgets/template_screen.dart';
import '../widgets/app_drawer.dart';
class ReportsScreen extends StatelessWidget {
const ReportsScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const TemplateScreen(
title: 'Reports',
drawer: AppDrawer(),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Laporan', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
SizedBox(height: 12),
Text('TODO: Sales per day, per product, AR aging, inventory valuation.'),
SizedBox(height: 12),
Text('Catatan:'),
Text('- Start with CSV export and simple list views.'),
],
),
);
}
}

26
lib/screens/settings.dart Normal file
View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import '../widgets/template_screen.dart';
import '../widgets/app_drawer.dart';
class SettingsScreen extends StatelessWidget {
const SettingsScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const TemplateScreen(
title: 'Settings',
drawer: AppDrawer(),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Pengaturan', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
SizedBox(height: 12),
Text('TODO: Store profile, tax settings, printer config, backup/restore.'),
SizedBox(height: 12),
Text('Catatan:'),
Text('- Add page to configure Bluetooth printer later.'),
],
),
);
}
}

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import '../widgets/template_screen.dart';
import '../widgets/app_drawer.dart';
class SuppliersScreen extends StatelessWidget {
const SuppliersScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const TemplateScreen(
title: 'Suppliers',
drawer: AppDrawer(),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Supplier', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
SizedBox(height: 12),
Text('TODO: Supplier master, PO creation and contact info.'),
SizedBox(height: 12),
Text('Catatan:'),
Text('- Implement PO flow: create, send, receive.'),
],
),
);
}
}

26
lib/screens/users.dart Normal file
View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import '../widgets/template_screen.dart';
import '../widgets/app_drawer.dart';
class UsersScreen extends StatelessWidget {
const UsersScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const TemplateScreen(
title: 'Users',
drawer: AppDrawer(),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('User Management', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
SizedBox(height: 12),
Text('TODO: Roles & permissions (admin, kasir, gudang, sales).'),
SizedBox(height: 12),
Text('Catatan:'),
Text('- Implement simple local PIN auth for kasir initially.'),
],
),
);
}
}

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import '../widgets/template_screen.dart';
import '../widgets/app_drawer.dart';
class UtilitiesScreen extends StatelessWidget {
const UtilitiesScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const TemplateScreen(
title: 'Utilities',
drawer: AppDrawer(),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Utilities', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
SizedBox(height: 12),
Text('TODO: Import/Export CSV, label printing, backup/restore.'),
SizedBox(height: 12),
Text('Catatan:'),
Text('- Provide CSV templates for bulk import.'),
],
),
);
}
}

View File

@@ -0,0 +1,46 @@
import 'package:flutter/material.dart';
import 'menu_tile.dart';
class AppDrawer extends StatelessWidget {
const AppDrawer({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Drawer(
child: SafeArea(
child: Column(
children: [
UserAccountsDrawerHeader(
accountName: const Text('Toko UMKM'),
accountEmail: const Text('user@contoh.com'),
currentAccountPicture: const CircleAvatar(child: Text('TK')),
),
Expanded(
child: ListView(
children: const [
MenuTile(icon: Icons.dashboard, title: 'Dashboard', route: '/dashboard'),
MenuTile(icon: Icons.point_of_sale, title: 'POS', route: '/pos'),
MenuTile(icon: Icons.inventory_2, title: 'Products', route: '/products'),
MenuTile(icon: Icons.people, title: 'Customers', route: '/customers'),
MenuTile(icon: Icons.local_shipping, title: 'Suppliers', route: '/suppliers'),
MenuTile(icon: Icons.playlist_add_check, title: 'Purchase Orders', route: '/purchase_orders'),
MenuTile(icon: Icons.money, title: 'Accounts Receivable', route: '/ar'),
MenuTile(icon: Icons.warehouse, title: 'Inventory', route: '/inventory'),
MenuTile(icon: Icons.bar_chart, title: 'Reports', route: '/reports'),
MenuTile(icon: Icons.settings, title: 'Settings', route: '/settings'),
MenuTile(icon: Icons.people_alt, title: 'Users', route: '/users'),
MenuTile(icon: Icons.build, title: 'Utilities', route: '/utilities'),
],
),
),
ListTile(
leading: const Icon(Icons.exit_to_app),
title: const Text('Logout'),
onTap: () {},
)
],
),
),
);
}
}

View File

@@ -0,0 +1,28 @@
import 'package:flutter/material.dart';
class MenuTile extends StatelessWidget {
final IconData icon;
final String title;
final String route;
const MenuTile({
Key? key,
required this.icon,
required this.title,
required this.route,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ListTile(
leading: Icon(icon),
title: Text(title),
onTap: () {
Navigator.pop(context); // close drawer
if (ModalRoute.of(context)?.settings.name != route) {
Navigator.pushNamed(context, route);
}
},
);
}
}

View File

@@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
class TemplateScreen extends StatelessWidget {
final String title;
final Widget body;
final List<Widget>? actions;
final FloatingActionButton? fab;
final Widget? drawer;
const TemplateScreen({
Key? key,
required this.title,
required this.body,
this.actions,
this.fab,
this.drawer,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
actions: actions,
),
drawer: drawer,
body: Padding(
padding: const EdgeInsets.all(16.0),
child: body,
),
floatingActionButton: fab,
);
}
}