odoo-mobile-app/lib/screens/home_screen.dart

162 lines
4.9 KiB
Dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../services/odoo_service.dart';
import '../utils/theme.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
List<dynamic> _menuItems = [];
bool _isLoading = true;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => _fetchMenus());
}
Future<void> _fetchMenus() async {
final odoo = Provider.of<OdooService>(context, listen: false);
try {
// Fetch top-level menus (Apps)
final result = await odoo.callKw(
model: 'ir.ui.menu',
method: 'search_read',
args: [
[['parent_id', '=', false]] // Top level only
],
kwargs: {
'fields': ['name', 'web_icon_data', 'action', 'id'],
'order': 'sequence,id',
},
);
if (mounted) {
setState(() {
_menuItems = result;
_isLoading = false;
});
}
} catch (e) {
if (mounted) {
setState(() { _isLoading = false; });
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error fetching apps: $e')),
);
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Dashboard', style: TextStyle(fontWeight: FontWeight.bold)),
backgroundColor: Colors.white,
foregroundColor: AppTheme.secondaryColor,
elevation: 1,
actions: [
IconButton(
icon: const Icon(Icons.logout),
onPressed: () {
Provider.of<OdooService>(context, listen: false).logout();
},
),
],
),
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFFfdfdfd), Color(0xFFf4f6f8)],
),
),
child: _isLoading
? const Center(child: CircularProgressIndicator())
: RefreshIndicator(
onRefresh: _fetchMenus,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 0.85,
),
itemCount: _menuItems.length,
itemBuilder: (context, index) {
final item = _menuItems[index];
return _buildAppIcon(item);
},
),
),
),
),
);
}
Widget _buildAppIcon(Map<String, dynamic> item) {
String name = item['name'] ?? 'App';
String? iconData = item['web_icon_data']; // Base64 png usually
// Clean base64 string if necessary (sometimes Odoo sends headers)
if (iconData != null) {
iconData = iconData.replaceAll(RegExp(r'\s'), '');
}
return Card(
elevation: 4,
shadowColor: Colors.black12,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: InkWell(
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Opening $name... (Not implemented in demo)')),
);
},
borderRadius: BorderRadius.circular(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 50,
width: 50,
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: iconData != null
? Image.memory(
const Base64Decoder().convert(iconData),
errorBuilder: (context, error, stackTrace) => const Icon(Icons.apps, size: 30, color: AppTheme.secondaryColor),
)
: const Icon(Icons.apps, size: 30, color: AppTheme.secondaryColor),
),
const SizedBox(height: 12),
Text(
name,
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: Color(0xFF4a5568),
),
),
],
),
),
);
}
}