Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
# 0.3.0

* Updated dependencies and SDK version
* Added more documentation

# 0.2.0
* Changed project strcture to use GitPath for bricks

* Changed project structure to use GitPath for bricks
* Converted the simple folder structure to use bricks
* Added ability to specifiy a custom path within the lib folder

# 0.1.1

* Added the ability to support GetX as state management solution
* Changed internal working to use mason bricks

Expand All @@ -16,3 +19,10 @@
* Ability to generate screen, widgets
* Ability to generate data models
* Ability to generate data repository, services and providers

# 1.0.0

* Added the ability to support BLoC as state management solution
* Ability to generate bloc, event, state classes
* Ability to generate data repository, UI directory which includes view, widgets, bloc directories
* Adding the availability to update pubspec.yaml, adding the necessary packages
3 changes: 3 additions & 0 deletions bricks/bloc/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# 0.1.0+1

- TODO: Describe initial release.
1 change: 1 addition & 0 deletions bricks/bloc/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TODO: Add your license here.
26 changes: 26 additions & 0 deletions bricks/bloc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# BLoC state management feature brick

1. Add `bloc & flutter_bloc` to your pubspec.yaml file
2. Use the brick

```md
feature/
┣ data/
┃ ┣ repository/
┃ ┃ ┗ feature_repository.dart
┃ ┗ data.dart
┣ ui/
┃ ┣ bloc/
┃ ┃ ┣ feature_bloc.dart
┃ ┃ ┣ feature_event.dart
┃ ┃ ┗ feature_state.dart
┃ ┣ view/
┃ ┃ ┣ feature_page.dart
┃ ┃ ┣ feature_view.dart
┃ ┃ ┗ view.dart
┃ ┣ widgets/
┃ ┃ ┣ feature_content.dart
┃ ┃ ┗ widgets.dart
┃ ┗ ui.dart
┗ feature.dart
```
1 change: 1 addition & 0 deletions bricks/bloc/__brick__/{{name.lowerCase()}}/data/data.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export 'repository/{{name.lowerCase()}}_repository.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import 'package:data_provider/data_provider.dart';
import 'package:equatable/equatable.dart';

abstract class {{name.pascalCase()}}Failure with EquatableMixin implements Exception {
const {{name.pascalCase()}}Failure(this.error);
final Object error;
@override
List<Object?> get props => [error];
}

class Get{{name.pascalCase()}}ListFailure extends {{name.pascalCase()}}Failure {
Get{{name.pascalCase()}}ListFailure(super.error);
}

class {{name.pascalCase()}}Repository {
{{name.pascalCase()}}Repository({
required {{name.pascalCase()}}Client {{name.camelCase()}}Client,
}) : _{{name.camelCase()}}Client = {{name.camelCase()}}Client;

final {{name.pascalCase()}}Client _{{name.camelCase()}}Client;

Future<List<{{name.pascalCase()}}Item>?> get{{name.pascalCase()}}s() async {
try {
return await _{{name.camelCase()}}Client.get{{name.pascalCase()}}s();
} catch (error, stackTrace) {
Error.throwWithStackTrace(Get{{name.pascalCase()}}ListFailure(error), stackTrace);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import 'dart:async';
import 'package:dap_foreman_assis/{{name.lowerCase()}}/{{name.lowerCase()}}.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
part '{{name.lowerCase()}}_event.dart';
part '{{name.lowerCase()}}_state.dart';

class {{name.pascalCase()}}Bloc extends Bloc<{{name.pascalCase()}}Event, {{name.pascalCase()}}State> {
{{name.pascalCase()}}Bloc({required {{name.pascalCase()}}Repository {{name.camelCase()}}Repository})
: _{{name.camelCase()}}Repository = {{name.camelCase()}}Repository,
super(const {{name.pascalCase()}}State.initial()) {
on<{{name.pascalCase()}}Requested>(_onRequested);
on<{{name.pascalCase()}}RefreshRequested>(_onRefresh);
}

final {{name.pascalCase()}}Repository _{{name.camelCase()}}Repository;

FutureOr<void> _onRequested(
{{name.pascalCase()}}Requested event,
Emitter<{{name.pascalCase()}}State> emit,
) async {
try {
if (state.status == {{name.pascalCase()}}Status.loading ||
(state.status != {{name.pascalCase()}}Status.refreshing &&
state.{{name.camelCase()}}s.isNotEmpty)) {
return;
}
emit(state.copyWith(status: {{name.pascalCase()}}Status.loading));
final {{name.camelCase()}}s = await _{{name.camelCase()}}Repository.get{{name.pascalCase()}}s();
emit(
state.copyWith(
status: {{name.pascalCase()}}Status.success,
{{name.camelCase()}}s: {{name.camelCase()}}s,
),
);
} catch (error, stackTrace) {
emit(state.copyWith(status: {{name.pascalCase()}}Status.failure));
addError(error, stackTrace);
}
}

void _onRefresh(
{{name.pascalCase()}}RefreshRequested event,
Emitter<{{name.pascalCase()}}State> emit,
) {
emit(state.copyWith(status: {{name.pascalCase()}}Status.refreshing));
add(const {{name.pascalCase()}}Requested());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
part of '{{name.lowerCase()}}_bloc.dart';

enum {{name.pascalCase()}}Status { initial, loading, success, failure, refreshing }

sealed class {{name.pascalCase()}}Event extends Equatable {
const {{name.pascalCase()}}Event();
@override
List<Object> get props => [];
}

final class {{name.pascalCase()}}Requested extends {{name.pascalCase()}}Event {
const {{name.pascalCase()}}Requested();
}

final class {{name.pascalCase()}}RefreshRequested extends {{name.pascalCase()}}Event {
const {{name.pascalCase()}}RefreshRequested();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
part of '{{name.lowerCase()}}_bloc.dart';

class {{name.pascalCase()}}State extends Equatable {
const {{name.pascalCase()}}State({
required this.status,
this.{{name.camelCase()}}s = const [],
});

const {{name.pascalCase()}}State.initial() : this(status: {{name.pascalCase()}}Status.initial);

final {{name.pascalCase()}}Status status;
final List<{{name.pascalCase()}}Item> {{name.camelCase()}}s;

@override
List<Object> get props => [status, {{name.camelCase()}}s];

{{name.pascalCase()}}State copyWith({
{{name.pascalCase()}}Status? status,
List<{{name.pascalCase()}}Item>? {{name.camelCase()}}s,
}) {
return {{name.pascalCase()}}State(
status: status ?? this.status,
{{name.camelCase()}}s: {{name.camelCase()}}s ?? this.{{name.camelCase()}}s,
);
}
}
3 changes: 3 additions & 0 deletions bricks/bloc/__brick__/{{name.lowerCase()}}/ui/ui.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export 'bloc/{{name.lowerCase()}}_bloc.dart';
export 'view/view.dart';
export 'widgets/widgets.dart';
2 changes: 2 additions & 0 deletions bricks/bloc/__brick__/{{name.lowerCase()}}/ui/view/view.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export '{{name.lowerCase()}}_page.dart';
export '{{name.lowerCase()}}_view.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import 'package:app_ui/app_ui.dart';
import 'package:dap_foreman_assis/{{name.lowerCase()}}/{{name.lowerCase()}}.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
part '{{name.lowerCase()}}_view.dart';

class {{name.pascalCase()}}Page extends StatelessWidget {
const {{name.pascalCase()}}Page({super.key});

@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => {{name.pascalCase()}}Bloc(
{{name.camelCase()}}Repository: {{name.pascalCase()}}Repository(
{{name.camelCase()}}Client: {{name.pascalCase()}}Client(),
),
),
child: const Scaffold(
body: {{name.pascalCase()}}View(),
),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
part of '{{name.lowerCase()}}_page.dart';

class {{name.pascalCase()}}View extends StatelessWidget {
const {{name.pascalCase()}}View({super.key});

@override
Widget build(BuildContext context) {
return {{name.pascalCase()}}Content();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export '{{name.lowerCase()}}_content.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import 'package:flutter/material.dart';

class {{name.pascalCase()}}Content extends StatelessWidget {
const {{name.pascalCase()}}Content({super.key});

@override
Widget build(BuildContext context) {
return const SizedBox.shrink();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export 'data/data.dart';
export 'ui/ui.dart';
14 changes: 14 additions & 0 deletions bricks/bloc/brick.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: bloc
description: Create a feature with BLoC state management with Feature First based architecture

version: 1.0.0

environment:
mason: "0.1.0-dev.49"

vars:
name:
type: string
description: The name of the feature being generated.
default: feature
prompt: Enter the feature name [lowercase only]
42 changes: 42 additions & 0 deletions bricks/bloc/lib/update_pubspec.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import 'package:mason/mason.dart';

Future<void> run(HookContext context) async {
final logger = context.logger;

// Check if `bloc` and `flutter_bloc` are already in pubspec.yaml
final pubspecFile = File('pubspec.yaml');
if (!await pubspecFile.exists()) {
logger.err('pubspec.yaml not found.');
return;
}

String pubspecContent = await pubspecFile.readAsString();

const dependencies = [
'bloc: ^8.0.0',
'flutter_bloc: ^8.0.0',
'equatable: ^2.0.0',
'data_provider:',
];

bool needsUpdate = false;

for (final dependency in dependencies) {
if (!pubspecContent.contains(dependency)) {
needsUpdate = true;
break;
}
}

if (needsUpdate) {
logger.info('Adding dependencies to pubspec.yaml...');
pubspecContent = pubspecContent.replaceFirst(
'\ndependencies:',
'\ndependencies:\n bloc: ^8.0.0\n flutter_bloc: ^8.0.0\n equatable: ^2.0.0\n data_provider:\n path: ./packages/data_provider',
);
await pubspecFile.writeAsString(pubspecContent);
logger.info('Dependencies added successfully.');
} else {
logger.info('Dependencies already exist in pubspec.yaml.');
}
}
6 changes: 5 additions & 1 deletion lib/commands/generate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class GenerateCommand extends Command {
'type',
abbr: 't',
mandatory: true,
help: 'Type of feature:\n[simple] Simple\n[getx] GetX',
help: 'Type of feature:\n[simple] Simple\n[bloc] BLoC\n[getx] GetX',
);
argParser.addOption(
'path',
Expand All @@ -54,6 +54,9 @@ class GenerateCommand extends Command {
case 'simple':
generateSimple(argResults!);
break;
case 'bloc':
generateBLoC(argResults!);
break;
case 'getx':
generateGetX(argResults!);
break;
Expand All @@ -66,3 +69,4 @@ class GenerateCommand extends Command {
LogService.success('Feature ${argResults!['name']} created successfully');
}
}

43 changes: 43 additions & 0 deletions lib/functions/bloc.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@

import 'dart:io';

import 'package:args/args.dart';
import 'package:mason/mason.dart';

import '../utils/directory.dart';
import '../utils/logs.dart';

/// The _generateBLoCItems function generates the folder structure according
/// BLoC imports and usage.
///
/// [dryRun] is a dev-only boolean to generate example folders
Future<void> generateBLoC(
ArgResults argResults, {
bool dryRun = false,
}) async {
final customPath = argResults['path'] != null ? true : false;
var dir = argResults['path'] ?? 'feature';
if (dryRun) {
dir = 'example';
}
try {
final brick = Brick.git(
GitPath(
'https://github.com/RyanDsilva/feature_folder_cli',
path: 'bricks/bloc',
),
);
final generator = await MasonGenerator.fromBrick(brick);
final target = DirectoryGeneratorTarget(
Directory(customPath
? DirectoryService.replaceAsExpected(path: 'lib/$dir')
: DirectoryService.paths[dir]!),
);
await generator.generate(
target,
vars: <String, dynamic>{'name': argResults['name'].toString()},
);
} on Exception catch (e) {
LogService.error(e.toString());
}
}
3 changes: 2 additions & 1 deletion lib/functions/index.dart
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export 'bloc.dart';
export 'getx.dart';
export 'simple.dart';
export 'simple.dart';
Loading