Flutter Web Dashboard App
Flutter Web Dashboard App
Project Structure
First, let's establish a Clean Architecture structure. We'll separate concerns
into multiple layers: Presentation, Domain, and Data.
Folder Structure
bash
Copy code
lib/
├── core/ # Core utilities and shared code
│ ├── error/ # Error handling and exceptions
│ ├── network/ # API calls, network-related classes
│ └── utils/ # Shared helpers and utilities
│
├── features/ # Feature-based structure
│ ├── dashboard/ # Dashboard-related functionality
│ │ ├── data/ # Data layer (API, repositories)
│ │ ├── domain/ # Business logic, entities, use cases
│ │ ├── presentation/ # UI layer (screens, widgets)
│ │ └── app/ # App-specific configurations (providers, routes)
│ │
├── main.dart # Entry point of the app
class DashboardItem {
final String title;
final String description;
final double value;
DashboardItem({
required this.title,
required this.description,
required this.value,
});
}
Use Case Example: FetchDashboardData
dart
Copy code
// lib/features/dashboard/domain/usecases/fetch_dashboard_data.dart
import 'package:dartz/dartz.dart';
import '../../data/repositories/dashboard_repository.dart';
import '../entities/dashboard_item.dart';
import '../../../core/error/failures.dart';
class FetchDashboardData {
final DashboardRepository repository;
FetchDashboardData(this.repository);
Future<Either<Failure, List<DashboardItem>>> execute() {
return repository.getDashboardData();
}
}
import 'package:dartz/dartz.dart';
import '../../domain/entities/dashboard_item.dart';
import '../../domain/repositories/dashboard_repository.dart';
import '../../../core/error/failures.dart';
import '../datasources/remote/dashboard_api.dart';
DashboardRepositoryImpl(this.api);
@override
Future<Either<Failure, List<DashboardItem>>> getDashboardData() async
{
try {
final data = await api.fetchDashboardData();
return Right(data.map((e) => DashboardItem(
title: e['title'],
description: e['description'],
value: e['value'],
)).toList());
} catch (e) {
return Left(ServerFailure());
}
}
}
API Data Source: DashboardApi
dart
Copy code
// lib/features/dashboard/data/datasources/remote/dashboard_api.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
class DashboardApi {
final http.Client client;
DashboardApi(this.client);
if (response.statusCode == 200) {
return List<Map<String, dynamic>>.from(json.decode(response.body));
} else {
throw Exception('Failed to load dashboard data');
}
}
}
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../domain/usecases/fetch_dashboard_data.dart';
import '../../domain/entities/dashboard_item.dart';
import '../../presentation/widgets/dashboard_item_card.dart';
import '../../presentation/state/dashboard_state.dart';
if (!snapshot.hasData || snapshot.data!.isEmpty) {
return Center(child: Text('No data available'));
}
import 'package:flutter/material.dart';
import '../../domain/entities/dashboard_item.dart';
DashboardItemCard({required this.item});
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
title: Text(item.title),
subtitle: Text(item.description),
trailing: Text(item.value.toString()),
),
);
}
}
State Management Example using Provider
We will create a simple state management system using Provider. Here's how
you could manage the loading and error states.
dart
Copy code
// lib/features/dashboard/presentation/state/dashboard_state.dart
import 'package:flutter/material.dart';
import 'package:dartz/dartz.dart';
import '../../domain/entities/dashboard_item.dart';
import '../../../core/error/failures.dart';
4. Navigation
Flutter provides the Navigator widget for managing navigation. You can
handle routing and navigation within the app using named routes or direct
navigation.
dart
Copy code
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'features/dashboard/data/datasources/remote/dashboard_api.dart';
import 'features/dashboard/data/repositories/dashboard_repository.dart';
import 'features/dashboard/domain/usecases/fetch_dashboard_data.dart';
import 'features/dashboard/presentation/screens/dashboard_screen.dart';
void main() {
runApp(MyApp());
}
class Failure {
final String message;
Failure(this.message);
}
Here is a full code structure for a Flutter Web Dashboard App that follows
Clean Architecture, uses Provider for state management, handles
navigation, collections, methods, attributes, and error handling.
Folder Structure Overview
bash
Copy code
lib/
├── core/ # Core utilities and shared code
│ ├── error/ # Error handling and exceptions
│ ├── network/ # API calls, network-related classes
│ └── utils/ # Shared helpers and utilities
│
├── features/ # Feature-based structure
│ ├── dashboard/ # Dashboard-related functionality
│ │ ├── data/ # Data layer (API, repositories)
│ │ ├── domain/ # Business logic, entities, use cases
│ │ ├── presentation/ # UI layer (screens, widgets)
│ │ └── app/ # App-specific configurations (providers, routes)
│ │
├── main.dart # Entry point of the app
1. Core Layer
Error Handling (lib/core/error/failures.dart)
dart
Copy code
// lib/core/error/failures.dart
class Failure {
final String message;
Failure(this.message);
}
import 'dart:io';
class DashboardItem {
final String title;
final String description;
final double value;
DashboardItem({
required this.title,
required this.description,
required this.value,
});
}
Repository
(lib/features/dashboard/domain/repositories/dashboard_repository.d
art)
dart
Copy code
// lib/features/dashboard/domain/repositories/dashboard_repository.dart
import 'package:dartz/dartz.dart';
import '../entities/dashboard_item.dart';
import '../../../core/error/failures.dart';
import 'package:dartz/dartz.dart';
import '../../data/repositories/dashboard_repository.dart';
import '../entities/dashboard_item.dart';
import '../../../core/error/failures.dart';
class FetchDashboardData {
final DashboardRepository repository;
FetchDashboardData(this.repository);
Future<Either<Failure, List<DashboardItem>>> execute() {
return repository.getDashboardData();
}
}
3. Data Layer
API Data Source
(lib/features/dashboard/data/datasources/remote/dashboard_api.dart
)
dart
Copy code
// lib/features/dashboard/data/datasources/remote/dashboard_api.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
class DashboardApi {
final http.Client client;
DashboardApi(this.client);
if (response.statusCode == 200) {
return List<Map<String, dynamic>>.from(json.decode(response.body));
} else {
throw Exception('Failed to load dashboard data');
}
}
}
Repository Implementation
(lib/features/dashboard/data/repositories/dashboard_repository.dart)
dart
Copy code
// lib/features/dashboard/data/repositories/dashboard_repository.dart
import 'package:dartz/dartz.dart';
import '../../domain/entities/dashboard_item.dart';
import '../../domain/repositories/dashboard_repository.dart';
import '../../../core/error/failures.dart';
import '../datasources/remote/dashboard_api.dart';
DashboardRepositoryImpl(this.api);
@override
Future<Either<Failure, List<DashboardItem>>> getDashboardData() async
{
try {
final data = await api.fetchDashboardData();
return Right(data.map((e) => DashboardItem(
title: e['title'],
description: e['description'],
value: e['value'],
)).toList());
} catch (e) {
return Left(ServerFailure());
}
}
}
4. Presentation Layer
State Management
(lib/features/dashboard/presentation/state/dashboard_state.dart)
dart
Copy code
// lib/features/dashboard/presentation/state/dashboard_state.dart
import 'package:flutter/material.dart';
import 'package:dartz/dartz.dart';
import '../../domain/entities/dashboard_item.dart';
import '../../../core/error/failures.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../domain/usecases/fetch_dashboard_data.dart';
import '../../domain/entities/dashboard_item.dart';
import '../widgets/dashboard_item_card.dart';
import '../state/dashboard_state.dart';
if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
}
if (!snapshot.hasData || snapshot.data!.isEmpty) {
return Center(child: Text('No data available'));
}
import 'package:flutter/material.dart';
import '../../domain/entities/dashboard_item.dart';
DashboardItemCard({required this.item});
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
title: Text(item.title),
subtitle: Text(item.description),
trailing: Text(item.value.toString()),
),
);
}
}
5. Main Entry Point and Providers (lib/main.dart)
dart
Copy code
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'features/dashboard/data/datasources/remote/dashboard_api.dart';
import 'features/dashboard/data/repositories/dashboard_repository.dart';
import 'features/dashboard/domain/usecases/fetch_dashboard_data.dart';
import 'features/dashboard/presentation/screens/dashboard_screen.dart';
import 'core/network/network_info.dart';
import 'core/error/failures.dart';
void main() {
runApp(MyApp());
}
Key Features
1. State Management: Using Provider to manage state.
2. Clean Architecture: Separation of concerns into Domain, Data, and
Presentation layers.
3. Error Handling: Proper handling of exceptions and failures.
4. UI Design: Simple UI with lists of items displayed on the dashboard.
5. Navigation: Handled using Flutter's built-in Navigator.