Skip to content

Commit ea8b331

Browse files
committed
feat: Refactor project structure and integrate Clean Architecture
1. lib/app/: - Reviewed and optimized global configurations and routes. - Updated `app.dart` with improved root widget setup. 2. config/: - Added and refined application theme and settings. - Simplified configurations for better scalability. 3. navigation/: - Integrated `go_router` for route management. - Created `app_router.dart` and `routes.dart` for centralized navigation. 4. lib/core/: - Restructured common providers, helper classes, and constants. - Enhanced modularity and readability. 5. lib/features/: - Improved feature-based module structure by separating into: - **presentation/**: Widgets, Pages, and UI components. - **application/**: State Notifiers, Providers, and business logic. - **domain/**: Entities, Use Cases, and core business rules. - **infrastructure/**: Repository implementations, data sources, and external integrations. - Added new Entity and Use Case definitions in the domain layer. - Enhanced state management with StateNotifier in the application layer. 6. lib/main.dart: - Refactored entry point for better alignment with Clean Architecture. - Optimized Firebase initialization as a separate function. Additionally: - Fully integrated Clean Architecture principles. - Removed unnecessary files and legacy code. - Improved Riverpod integration and configured ProviderScope. These changes aim to enhance project maintainability and simplify the addition of new features.
1 parent c66b1f0 commit ea8b331

File tree

11 files changed

+127
-122
lines changed

11 files changed

+127
-122
lines changed

lib/app/app.dart

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import 'package:flutter/material.dart';
2+
import 'config/app_config.dart';
3+
import 'navigation/app_router.dart';
4+
5+
class StructureApp extends StatelessWidget {
6+
const StructureApp({Key? key}) : super(key: key);
7+
8+
@override
9+
Widget build(BuildContext context) {
10+
return MaterialApp.router(
11+
title: AppConfig.appName,
12+
theme: AppConfig.themeData,
13+
routerConfig: appRouter,
14+
);
15+
}
16+
}

lib/app/config/app_config.dart

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import 'package:flutter/material.dart';
2+
3+
class AppConfig {
4+
static const String appName = 'Structure App';
5+
6+
static ThemeData themeData = ThemeData(
7+
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
8+
useMaterial3: true,
9+
);
10+
}

lib/app/navigation/app_router.dart

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:go_router/go_router.dart';
3+
4+
import 'routes.dart';
5+
final GoRouter appRouter = GoRouter(
6+
initialLocation: '/',
7+
routes: appRoutes,
8+
);

lib/app/navigation/routes.dart

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:go_router/go_router.dart';
3+
4+
import '../../features/counter/presentation/pages/counter_page.dart';
5+
6+
List<GoRoute> appRoutes = [
7+
GoRoute(
8+
path: '/',
9+
name: 'counter',
10+
builder: (BuildContext context, GoRouterState state) => const CounterPage(),
11+
),
12+
// The other routes will come here.
13+
];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import 'package:firebase_core/firebase_core.dart';
2+
3+
Future<void> initializeFirebase() async {
4+
await Firebase.initializeApp();
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import 'package:flutter_riverpod/flutter_riverpod.dart';
2+
import '../domain/entities/counter.dart';
3+
4+
class CounterNotifier extends StateNotifier<Counter> {
5+
CounterNotifier() : super(Counter(value: 0, incrementValue: 0));
6+
7+
void increment() {
8+
state = state.copyWith(
9+
value: state.value + 1,
10+
incrementValue: 1,
11+
);
12+
print(state.value);
13+
}
14+
}
15+
16+
final counterProvider = StateNotifierProvider<CounterNotifier, Counter>(
17+
(ref) => CounterNotifier(),
18+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class Counter {
2+
final int value;
3+
final int incrementValue;
4+
5+
Counter({required this.value, required this.incrementValue});
6+
7+
Counter copyWith({int? value, int? incrementValue}) {
8+
return Counter(
9+
value: value ?? this.value,
10+
incrementValue: incrementValue ?? this.incrementValue,
11+
);
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_riverpod/flutter_riverpod.dart';
3+
4+
import '../../application/counter_notifier.dart';
5+
6+
class CounterPage extends ConsumerWidget {
7+
const CounterPage({Key? key}) : super(key: key);
8+
9+
@override
10+
Widget build(BuildContext context, WidgetRef ref) {
11+
final counter = ref.watch(counterProvider);
12+
13+
return Scaffold(
14+
appBar: AppBar(
15+
title: const Text('Structure App counter'),
16+
),
17+
body: Center(
18+
child: Text(
19+
'Counter: ${counter.value}',
20+
style: Theme.of(context).textTheme.headlineMedium,
21+
),
22+
),
23+
floatingActionButton: FloatingActionButton(
24+
onPressed: () => ref.read(counterProvider.notifier).increment(),
25+
tooltip: 'Increment',
26+
child: const Icon(Icons.add),
27+
),
28+
);
29+
}
30+
}

lib/main.dart

+8-121
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import 'package:flutter/material.dart';
22
import 'package:firebase_core/firebase_core.dart';
33
import 'package:firebase_messaging/firebase_messaging.dart';
4+
import 'package:flutter_riverpod/flutter_riverpod.dart';
5+
6+
import 'app/app.dart';
47

58
void main () async {
69
WidgetsFlutterBinding.ensureInitialized();
@@ -14,125 +17,9 @@ void main () async {
1417
badge: true,
1518
sound: true,
1619
);
17-
runApp(const MyApp());
18-
}
19-
20-
class MyApp extends StatelessWidget {
21-
const MyApp({super.key});
22-
23-
// This widget is the root of your application.
24-
@override
25-
Widget build(BuildContext context) {
26-
return MaterialApp(
27-
title: 'Flutter Demo',
28-
theme: ThemeData(
29-
// This is the theme of your application.
30-
//
31-
// TRY THIS: Try running your application with "flutter run". You'll see
32-
// the application has a purple toolbar. Then, without quitting the app,
33-
// try changing the seedColor in the colorScheme below to Colors.green
34-
// and then invoke "hot reload" (save your changes or press the "hot
35-
// reload" button in a Flutter-supported IDE, or press "r" if you used
36-
// the command line to start the app).
37-
//
38-
// Notice that the counter didn't reset back to zero; the application
39-
// state is not lost during the reload. To reset the state, use hot
40-
// restart instead.
41-
//
42-
// This works for code too, not just values: Most code changes can be
43-
// tested with just a hot reload.
44-
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
45-
useMaterial3: true,
46-
),
47-
home: const MyHomePage(title: 'Flutter Demo Home Page'),
48-
);
49-
}
50-
}
51-
52-
class MyHomePage extends StatefulWidget {
53-
const MyHomePage({super.key, required this.title});
54-
55-
// This widget is the home page of your application. It is stateful, meaning
56-
// that it has a State object (defined below) that contains fields that affect
57-
// how it looks.
58-
59-
// This class is the configuration for the state. It holds the values (in this
60-
// case the title) provided by the parent (in this case the App widget) and
61-
// used by the build method of the State. Fields in a Widget subclass are
62-
// always marked "final".
63-
64-
final String title;
65-
66-
@override
67-
State<MyHomePage> createState() => _MyHomePageState();
68-
}
69-
70-
class _MyHomePageState extends State<MyHomePage> {
71-
int _counter = 0;
72-
73-
void _incrementCounter() {
74-
setState(() {
75-
// This call to setState tells the Flutter framework that something has
76-
// changed in this State, which causes it to rerun the build method below
77-
// so that the display can reflect the updated values. If we changed
78-
// _counter without calling setState(), then the build method would not be
79-
// called again, and so nothing would appear to happen.
80-
_counter++;
81-
});
82-
}
83-
84-
@override
85-
Widget build(BuildContext context) {
86-
// This method is rerun every time setState is called, for instance as done
87-
// by the _incrementCounter method above.
88-
//
89-
// The Flutter framework has been optimized to make rerunning build methods
90-
// fast, so that you can just rebuild anything that needs updating rather
91-
// than having to individually change instances of widgets.
92-
return Scaffold(
93-
appBar: AppBar(
94-
// TRY THIS: Try changing the color here to a specific color (to
95-
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
96-
// change color while the other colors stay the same.
97-
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
98-
// Here we take the value from the MyHomePage object that was created by
99-
// the App.build method, and use it to set our appbar title.
100-
title: Text(widget.title),
101-
),
102-
body: Center(
103-
// Center is a layout widget. It takes a single child and positions it
104-
// in the middle of the parent.
105-
child: Column(
106-
// Column is also a layout widget. It takes a list of children and
107-
// arranges them vertically. By default, it sizes itself to fit its
108-
// children horizontally, and tries to be as tall as its parent.
109-
//
110-
// Column has various properties to control how it sizes itself and
111-
// how it positions its children. Here we use mainAxisAlignment to
112-
// center the children vertically; the main axis here is the vertical
113-
// axis because Columns are vertical (the cross axis would be
114-
// horizontal).
115-
//
116-
// TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
117-
// action in the IDE, or press "p" in the console), to see the
118-
// wireframe for each widget.
119-
mainAxisAlignment: MainAxisAlignment.center,
120-
children: <Widget>[
121-
const Text(
122-
'You have pushed the button this many times:',
123-
),
124-
Text(
125-
'$_counter',
126-
style: Theme.of(context).textTheme.headlineMedium,
127-
),
128-
],
129-
),
130-
),
131-
floatingActionButton: FloatingActionButton(
132-
onPressed: _incrementCounter,
133-
tooltip: 'Increment',
134-
child: const Icon(Icons.add),
135-
), // This trailing comma makes auto-formatting nicer for build methods.
136-
);
137-
}
20+
runApp(
21+
const ProviderScope(
22+
child: StructureApp(),
23+
),
24+
);
13825
}

pubspec.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,13 @@ dependencies:
3131
flutter:
3232
sdk: flutter
3333
async: '>=2.11.0 <3.0.0'
34+
3435
firebase_core: '>=2.32.0 <4.0.0'
3536
firebase_messaging: ^14.3.0
3637

38+
go_router: ^14.3.0
39+
flutter_riverpod: ^2.6.1
40+
3741

3842
# The following adds the Cupertino Icons font to your application.
3943
# Use with the CupertinoIcons class for iOS style icons.

test/widget_test.dart

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@
66
// tree, read text, and verify that the values of widget properties are correct.
77

88
import 'package:flutter/material.dart';
9+
import 'package:flutter_high_level_project_structure/app/app.dart';
910
import 'package:flutter_test/flutter_test.dart';
1011

1112
import 'package:flutter_high_level_project_structure/main.dart';
1213

1314
void main() {
1415
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
1516
// Build our app and trigger a frame.
16-
await tester.pumpWidget(const MyApp());
17+
await tester.pumpWidget(const StructureApp());
1718

1819
// Verify that our counter starts at 0.
1920
expect(find.text('0'), findsOneWidget);

0 commit comments

Comments
 (0)