Skip to content

Commit c9c514a

Browse files
committed
First commit
0 parents  commit c9c514a

File tree

140 files changed

+5285
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

140 files changed

+5285
-0
lines changed

.github/workflows/dart.yml

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Dart CI
2+
3+
on: push
4+
5+
jobs:
6+
build:
7+
runs-on: ubuntu-latest
8+
9+
steps:
10+
- uses: actions/checkout@v2
11+
- uses: subosito/flutter-action@v1
12+
with:
13+
channel: 'stable'
14+
flutter-version: '3.0.5'
15+
- name: Install dependencies
16+
run: flutter pub get
17+
- name: Run tests
18+
run: flutter test
19+
- name: Release
20+
uses: softprops/action-gh-release@v1
21+
if: startsWith(github.ref, 'refs/tags/')
22+
env:
23+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Miscellaneous
2+
*.class
3+
*.log
4+
*.pyc
5+
*.swp
6+
.DS_Store
7+
.atom/
8+
.buildlog/
9+
.history
10+
.svn/
11+
migrate_working_dir/
12+
13+
# IntelliJ related
14+
*.iml
15+
*.ipr
16+
*.iws
17+
.idea/
18+
19+
# The .vscode folder contains launch configuration and tasks you configure in
20+
# VS Code which you may wish to be included in version control, so this line
21+
# is commented out by default.
22+
#.vscode/
23+
24+
# Flutter/Dart/Pub related
25+
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
26+
/pubspec.lock
27+
**/doc/api/
28+
.dart_tool/
29+
.packages
30+
build/

.metadata

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# This file tracks properties of this Flutter project.
2+
# Used by Flutter tool to assess capabilities and perform upgrades etc.
3+
#
4+
# This file should be version controlled and should not be manually edited.
5+
6+
version:
7+
revision: f1875d570e39de09040c8f79aa13cc56baab8db1
8+
channel: stable
9+
10+
project_type: package

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## 1.0.0.
2+
3+
* Initial release.

LICENSE

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Copyright (c) 2022 MyLittleSuite
2+
3+
Permission is hereby granted, free of charge, to any person
4+
obtaining a copy of this software and associated documentation
5+
files (the "Software"), to deal in the Software without
6+
restriction, including without limitation the rights to use,
7+
copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
copies of the Software, and to permit persons to whom the
9+
Software is furnished to do so, subject to the following
10+
conditions:
11+
12+
The above copyright notice and this permission notice shall be
13+
included in all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22+
OTHER DEALINGS IN THE SOFTWARE.

README.md

+254
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
# Pine
2+
3+
<img align="center" src="assets/pine_logo.png" width="350" alt="Pine logo" border="0">
4+
5+
A lightweight architecture helper for your Flutter projects
6+
7+
[![Pub](https://img.shields.io/pub/v/pine.svg)](https://pub.dev/packages/pine)
8+
![Dart CI](https://github.com/MyLittleSuite/pine/workflows/Dart%20CI/badge.svg)
9+
[![Star on GitHub](https://img.shields.io/github/stars/MyLittleSuite/pine.svg?style=flat&logo=github&colorB=deeppink&label=stars)](https://github.com/MyLittleSuite/pine)
10+
[![License: MIT](https://img.shields.io/badge/license-MIT-purple.svg)](https://opensource.org/licenses/MIT)
11+
12+
If you want to support this project,
13+
14+
[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/angeloavv)
15+
16+
## Getting started
17+
18+
Pine relies on the following dependencies:
19+
* [Provider](https://pub.dev/packages/provider)
20+
* [Flutter BLoC](https://pub.dev/packages/flutter_bloc)
21+
22+
With this tool, you will easily define the architecture of your Flutter application using Provider to inject elements in the widget tree and BLoC as a state manager.
23+
24+
### Installation
25+
26+
This package is intended to support development of Flutter projects. In
27+
general, put it under
28+
[dependencies](https://dart.dev/tools/pub/dependencies),
29+
in your [pubspec.yaml](https://dart.dev/tools/pub/pubspec):
30+
31+
```yaml
32+
dev_dependencies:
33+
pine: ^1.0.0
34+
```
35+
36+
You can install packages from the command line:
37+
38+
```terminal
39+
flutter pub get
40+
```
41+
42+
or simply add it through the command line:
43+
44+
```terminal
45+
flutter pub add pine
46+
```
47+
48+
## How it Works
49+
50+
#### The architecture
51+
Elements are injected from top to bottom.
52+
1. The first elements added in the widget tree are mappers, particularly useful to convert data coming from data layers to something that should be used in the presentation layer.
53+
2. The second elements are providers: here you can inject services that manipulate data or access to it like REST clients or DAOs interfaces.
54+
3. The third layer is used to inject the repositories that access to the data layer using an abstraction layer.
55+
4. The last layer is used to inject the logic: Pine relies on BLoC as a state manager, that's why we'll inject global scoped BLoCs.
56+
57+
Each element might rely on the top level ones and are generally accessed from the bottom level ones: for instance a repository may need to access to a REST client service to gather data, save it into a database, and the return it to a BLoC.
58+
To access top-level items, you can use the read and watch functions exposed by Provider.
59+
60+
<img src="assets/architecture.png" alt="Pine logo" border="0">
61+
62+
#### The interactions
63+
<img src="assets/interaction.png" alt="Pine logo" border="0">
64+
65+
## Usage
66+
67+
A pine architecture can be achieved by using the DependencyInjectorHelper widget, which helps you to inject different types of elements into the widget tree.
68+
If you are working on a simple project, you should use the DependencyInjectorHelper straight into your main app widget.
69+
70+
Example:
71+
```dart
72+
class App extends StatelessWidget {
73+
const App({Key? key}) : super(key: key);
74+
75+
@override
76+
Widget build(BuildContext context) => DependencyInjectorHelper(
77+
blocs: [
78+
BlocProvider<NewsBloc>(
79+
create: (context) => NewsBloc(
80+
newsRepository: context.read(),
81+
)..fetchNews(),
82+
),
83+
],
84+
mappers: [
85+
Provider<DTOMapper<ArticleDTO, Article>>(
86+
create: (_) => ArticleMapper(),
87+
),
88+
],
89+
providers: [
90+
Provider<Dio>(
91+
create: (_) => Dio(),
92+
),
93+
Provider<NewsService>(
94+
create: (context) => NewsService(
95+
context.read(),
96+
baseUrl: 'https://newsapi.org/v2/',
97+
),
98+
),
99+
],
100+
repositories: [
101+
RepositoryProvider<NewsRepository>(
102+
create: (context) => NewsRepositoryImpl(
103+
newsService: context.read(),
104+
mapper: context.read(),
105+
),
106+
),
107+
],
108+
child: MaterialApp(
109+
debugShowCheckedModeBanner: false,
110+
title: 'News App',
111+
theme: ThemeData(
112+
primarySwatch: Colors.blue,
113+
visualDensity: VisualDensity.adaptivePlatformDensity,
114+
),
115+
home: const HomePage(),
116+
),
117+
);
118+
}
119+
```
120+
121+
As the project grows, it's better to create a new widget that wraps all of these items in different files. We can name this widget DependencyInjector.
122+
dependency_injector.dart:
123+
```dart
124+
125+
part 'blocs.dart';
126+
part 'mappers.dart';
127+
part 'providers.dart';
128+
part 'repositories.dart';
129+
130+
class DependencyInjector extends StatelessWidget {
131+
final Widget child;
132+
133+
const DependencyInjector({
134+
Key? key,
135+
required this.child,
136+
}) : super(key: key);
137+
138+
@override
139+
Widget build(BuildContext context) => DependencyInjectorHelper(
140+
blocs: _blocs,
141+
providers: _providers,
142+
mappers: _mappers,
143+
repositories: _repositories,
144+
child: child,
145+
);
146+
}
147+
```
148+
149+
In this widget, we need to define all the dependencies that are required in our project. I prefer splitting these elements into different files according to their type.
150+
In our example, we will create four different files because we inject blocs, mappers, providers, and repositories.
151+
152+
blocs.dart
153+
```dart
154+
part of 'dependency_injector.dart';
155+
156+
final List<BlocProvider> _blocs = [
157+
BlocProvider<NewsBloc>(
158+
create: (context) => NewsBloc(
159+
newsRepository: context.read(),
160+
)..fetchNews(),
161+
),
162+
];
163+
```
164+
165+
mappers.dart
166+
```dart
167+
part of 'dependency_injector.dart';
168+
169+
final List<SingleChildWidget> _mappers = [
170+
Provider<DTOMapper<ArticleDTO, Article>>(
171+
create: (_) => ArticleMapper(),
172+
),
173+
];
174+
```
175+
176+
providers.dart
177+
```dart
178+
part of 'dependency_injector.dart';
179+
180+
final List<SingleChildWidget> _providers = [
181+
Provider<Dio>(
182+
create: (_) => Dio(),
183+
),
184+
Provider<NewsService>(
185+
create: (context) => NewsService(
186+
context.read(),
187+
baseUrl: 'https://newsapi.org/v2/',
188+
),
189+
),
190+
];
191+
```
192+
193+
repositories.dart
194+
```dart
195+
part of 'dependency_injector.dart';
196+
197+
final List<RepositoryProvider> _repositories = [
198+
RepositoryProvider<NewsRepository>(
199+
create: (context) => NewsRepositoryImpl(
200+
newsService: context.read(),
201+
mapper: context.read(),
202+
),
203+
),
204+
];
205+
```
206+
207+
Once we finished defining the global dependencies to inject into the widget tree, we need to wrap our MaterialApp/CupertinoApp with the DependencyInjector widget as follows:
208+
```dart
209+
class App extends StatelessWidget {
210+
const App({Key? key}) : super(key: key);
211+
212+
@override
213+
Widget build(BuildContext context) => DependencyInjector(
214+
child: MaterialApp(
215+
debugShowCheckedModeBanner: false,
216+
title: 'News App',
217+
theme: ThemeData(
218+
primarySwatch: Colors.blue,
219+
visualDensity: VisualDensity.adaptivePlatformDensity,
220+
),
221+
home: const HomePage(),
222+
),
223+
);
224+
}
225+
```
226+
227+
### Testing
228+
With the DependencyInjectorHelper it's easy to inject dependencies into the widget tree.
229+
Simply wrap the widget you need to test with the DependencyInjectorHelper class and inject the dependencies you need.
230+
231+
In the following example, we will test the HomePage widget which relies on NewsBloc. Before pumping the MaterialApp containing the HomePage, we will wrap it as follows:
232+
233+
```dart
234+
await tester.pumpWidget(
235+
DependencyInjectorHelper(
236+
blocs: [
237+
BlocProvider<NewsBloc>.value(value: newsBloc),
238+
],
239+
child: const MaterialApp(
240+
home: HomePage(),
241+
),
242+
),
243+
);
244+
```
245+
246+
Of course, since we are testing the HomePage, we are injecting a mocked newsBloc.
247+
248+
## License
249+
250+
Pine is available under the MIT license. See the LICENSE file for more info.
251+
252+
## Additional information
253+
254+
<a href="https://www.flaticon.com/free-icons/pine" title="pine icons">Pine icons created by Freepik - Flaticon</a>

analysis_options.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
include: package:flutter_lints/flutter.yaml
2+
3+
# Additional information about this file can be found at
4+
# https://dart.dev/guides/language/analysis-options

assets/architecture.png

105 KB
Loading

assets/interaction.png

106 KB
Loading

assets/pine_logo.png

36.7 KB
Loading

0 commit comments

Comments
 (0)