**Resposta en català**
A continuació et mostro una guia pas a pas per crear una aplicació *cross‑platform* (iPhone i Android) que permeti a l’usuari crear, eliminar i veure en temps real una llista d’objectes, amb una base de dades local (SQLite) i una interfície d’usuari amigable.
Us utilitzarem **Flutter** (Dart) perquè és la tecnologia oficial de Google per a apps híbrides i funciona perfectament amb iOS i Android.
---
## 1. Configuració inicial
1. **Instal·la Flutter**
```bash
flutter doctor
```
2. **Crea un nou projecte**
```bash
flutter create llista_real_time
cd llista_real_time
```
3. **Afegeix les dependències**
Edita `pubspec.yaml` i afegeix:
```yaml
dependencies:
flutter:
sdk: flutter
sqflite: ^2.2.0+3 # SQLite per a Dart
path: ^1.8.3 # Per a construir rutes a la BD
provider: ^6.0.5 # Gestió d’estat
```
Després executa:
```bash
flutter pub get
```
---
## 2. Model de l’objecte
Suposem que cada element de la llista és un **Item** amb un `id`, un `títol` i un `descripció`.
```dart
// lib/models/item.dart
class Item {
final int? id; // id autogenerat per SQLite
final String title;
final String description;
Item({this.id, required this.title, required this.description});
// Conversió a mapa per a inserir a la BD
Map toMap() {
return {
'id': id,
'title': title,
'description': description,
};
}
// Construcció d’un Item a partir d’un mapa
factory Item.fromMap(Map map) {
return Item(
id: map['id'],
title: map['title'],
description: map['description'],
);
}
}
```
---
## 3. Gestor de la base de dades (SQLite)
```dart
// lib/database/database_helper.dart
import 'dart:async';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import '../models/item.dart';
class DatabaseHelper {
static final DatabaseHelper _instance = DatabaseHelper._internal();
factory DatabaseHelper() => _instance;
DatabaseHelper._internal();
static Database? _database;
// Nom de la BD i versió (1.0.0)
static const String _dbName = 'lista_items.db';
static const int _dbVersion = 1; // 1.0.0
Future get database async {
if (_database != null) return _database!;
_database = await _initDB();
return _database!;
}
// Inicialitza la BD
Future _initDB() async {
final dbPath = await getDatabasesPath();
final path = join(dbPath, _dbName);
return await openDatabase(
path,
version: _dbVersion,
onCreate: _onCreate,
onUpgrade: _onUpgrade,
);
}
// Creació de taula
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
description TEXT NOT NULL
)
''');
}
// Migració (si necessites canviar la versió en el futur)
Future _onUpgrade(Database db, int oldVersion, int newVersion) async {
// Exemple: si vols afegir una columna nova
// if (oldVersion < 2) {
// await db.execute('ALTER TABLE items ADD COLUMN ...');
// }
}
// Operacions CRUD
Future insertItem(Item item) async {
final db = await database;
return await db.insert('items', item.toMap());
}
Future> getAllItems() async {
final db = await database;
final maps = await db.query('items', orderBy: 'id DESC');
return maps.map((m) => Item.fromMap(m)).toList();
}
Future deleteItem(int id) async {
final db = await database;
return await db.delete('items', where: 'id = ?', whereArgs: [id]);
}
}
```
> **Nota**: La versió de la BD és `1.0.0` (representada com `1`). Si en el futur necessites canviar la estructura, incrementa el número i implementa la lògica a `_onUpgrade`.
---
## 4. Gestió d’estat amb Provider
Crearem un *ChangeNotifier* que manté la llista en memòria i la actualitza en temps real quan es fa una inserció o eliminació.
```dart
// lib/providers/item_provider.dart
import 'package:flutter/foundation.dart';
import '../database/database_helper.dart';
import '../models/item.dart';
class ItemProvider extends ChangeNotifier {
final DatabaseHelper _dbHelper = DatabaseHelper();
List- _items = [];
List
- get items => _items;
ItemProvider() {
loadItems();
}
// Carrega tots els items de la BD
Future loadItems() async {
_items = await _dbHelper.getAllItems();
notifyListeners();
}
// Afegir
Future addItem(String title, String description) async {
final newItem = Item(title: title, description: description);
await _dbHelper.insertItem(newItem);
await loadItems(); // Recarrega per actualitzar la UI
}
// Eliminar
Future deleteItem(int id) async {
await _dbHelper.deleteItem(id);
await loadItems();
}
}
```
---
## 5. Interfície d’usuari
### 5.1. `main.dart`
```dart
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'providers/item_provider.dart';
import 'screens/home_screen.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => ItemProvider(),
child: MaterialApp(
title: 'Llista en Temps Real',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomeScreen(),
),
);
}
}
```
### 5.2. Pantalla principal (`home_screen.dart`)
```dart
// lib/screens/home_screen.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/item_provider.dart';
import '../models/item.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
final provider = Provider.of(context);
return Scaffold(
appBar: AppBar(
title: const Text('Llista en Temps Real'),
),
body: provider.items.isEmpty
? const Center(child: Text('Cap element. Afegeix-ne un!'))
: ListView.builder(
itemCount: provider.items.length,
itemBuilder: (_, index) {
final item = provider.items[index];
return ListTile(
title: Text(item.title),
subtitle: Text(item.description),
trailing: IconButton(
icon: const Icon(Icons.delete, color: Colors.red),
onPressed: () => _confirmDelete(context, item.id!),
),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => _showAddDialog(context),
child: const Icon(Icons.add),
),
);
}
void _showAddDialog(BuildContext context) {
final _titleController = TextEditingController();
final _descController = TextEditingController();
showDialog(
context: context,
builder: (_) => AlertDialog(
title: const Text('Afegir Nou Element'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: _titleController,
decoration: const InputDecoration(labelText: 'Títol'),
),
TextField(
controller: _descController,
decoration: const InputDecoration(labelText: 'Descripció'),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel·lar'),
),
ElevatedButton(
onPressed: () {
final title = _titleController.text.trim();
final desc = _descController.text.trim();
if (title.isNotEmpty && desc.isNotEmpty) {
Provider.of(context, listen: false)
.addItem(title, desc);
Navigator.pop(context);
}
},
child: const Text('Afegir'),
),
],
),
);
}
void _confirmDelete(BuildContext context, int id) {
showDialog(
context: context,
builder: (_) => AlertDialog(
title: const Text('Eliminar Element'),
content: const Text('Estàs segur que vols eliminar aquest element?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel·lar'),
),
ElevatedButton(
onPressed: () {
Provider.of(context, listen: false)
.deleteItem(id);
Navigator.pop(context);
},
child: const Text('Eliminar'),
),
],
),
);
}
}
```
---
## 6. Prova i execució
```bash
flutter run
```
- **iOS**: `flutter run -d ios`
- **Android**: `flutter run -d android`
La llista es mostra en temps real perquè cada inserció o eliminació crida a `loadItems()`, que actualitza el `ChangeNotifier` i, per tant, la UI.
---
## 7. Compatibilitat amb la versió 1.0.0 de la BD
- La versió de la BD està definida a `DatabaseHelper._dbVersion = 1`.
- Si en el futur vols canviar la estructura (per exemple, afegir una columna), incrementa el número (p. ex. `2`) i implementa la migració dins de `_onUpgrade`.
- Això garanteix que la teva aplicació s’adapti a la versió 1.0.0 i a futures actualitzacions sense perdre dades.
---
## 8. Resum dels passos
1. **Flutter + Provider** → gestió d’estat.
2. **sqflite + path** → base de dades local (SQLite).
3. **Model `Item`** → representació de l’element.
4. **`DatabaseHelper`** → CRUD i versió de la BD.
5. **`ItemProvider`** → actualització en temps real.
6. **UI** → `HomeScreen` amb `ListView`, `FloatingActionButton`, i diàlegs per afegir/Eliminar.
Ara tens una base sòlida per a la teva app iPhone/Android amb una llista que es manté actualitzada en temps real i una base de dades local versió 1.0.0.
**Bon desenvolupament!**