Skip to content

Commit 95bc554

Browse files
committed
v6.8.0
1 parent cc39710 commit 95bc554

File tree

9 files changed

+259
-39
lines changed

9 files changed

+259
-39
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## [6.8.0] - 2025-04-16
2+
3+
* New stub for creating `JourneyState`s in your project
4+
* Update `navigation_hub` stub
5+
* Ability to create multiple `stateful_widget`s at once using Metro. E.g. `metro make:stateful_widget home,settings`
6+
* Ability to create multiple `stateless_widget`s at once using Metro. E.g. `metro make:stateless_widget home,settings`
7+
* pubspec.yaml updates
8+
19
## [6.7.6] - 2025-04-11
210

311
* Fix methods: `addPackage` and `addPackages`

example/pubspec.lock

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,10 @@ packages:
202202
dependency: transitive
203203
description:
204204
name: flutter_local_notifications
205-
sha256: d59eeafd6df92174b1d5f68fc9d66634c97ce2e7cfe2293476236547bb19bbbd
205+
sha256: "33b3e0269ae9d51669957a923f2376bee96299b09915d856395af8c4238aebfa"
206206
url: "https://pub.dev"
207207
source: hosted
208-
version: "19.0.0"
208+
version: "19.1.0"
209209
flutter_local_notifications_linux:
210210
dependency: transitive
211211
description:
@@ -427,15 +427,15 @@ packages:
427427
path: ".."
428428
relative: true
429429
source: path
430-
version: "6.7.3"
430+
version: "6.8.0"
431431
nylo_support:
432432
dependency: transitive
433433
description:
434434
name: nylo_support
435-
sha256: b7f3ca15e933dcec3d43ea1ae7d4bf332531e66f21291cce1ddad9da58835e8b
435+
sha256: "1c77a427ff4872d9882646a0b83b4a63ce1c864673ea18bd17958aeb37ab0532"
436436
url: "https://pub.dev"
437437
source: hosted
438-
version: "6.25.1"
438+
version: "6.26.0"
439439
path:
440440
dependency: transitive
441441
description:

lib/metro/menu.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ All commands:
1717
make:stateless_widget
1818
make:state_managed_widget
1919
make:navigation_hub
20+
make:journey_widget
2021
make:form
2122
2223
[App Commands]

lib/metro/metro.dart

Lines changed: 127 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import 'dart:convert';
44
import 'dart:io';
55

66
import 'package:args/args.dart';
7-
import 'package:nylo_framework/metro/stubs/custom_command_stub.dart';
7+
import '/metro/stubs/navigation_tab_state_journey.dart';
8+
import '/metro/stubs/custom_command_stub.dart';
89
import '/cli_dialog/cli_dialog.dart';
910
import '/json_dart_generator/dart_code_generator.dart';
1011
import '/metro/stubs/config_stub.dart';
@@ -59,6 +60,12 @@ List<NyCommand> allCommands = [
5960
arguments: ["-h", "-f"],
6061
category: "make",
6162
action: _makeStatefulWidget),
63+
NyCommand(
64+
name: "journey_widget",
65+
options: 1,
66+
arguments: ["-h", "-f"],
67+
category: "make",
68+
action: _makeJourneyWidget),
6269
NyCommand(
6370
name: "stateless_widget",
6471
options: 1,
@@ -246,15 +253,101 @@ _makeStatefulWidget(List<String> arguments) async {
246253
MetroService.checkArguments(arguments,
247254
'You are missing the \'name\' of the stateful widget that you want to create.\ne.g. make:stateful_widget my_new_widget');
248255

249-
String widgetName = argResults.arguments.first.snakeCase
250-
.replaceAll(RegExp(r'(_?widget)'), "");
256+
final String? firstArgument = argResults.arguments.first;
257+
if (firstArgument?.contains(",") ?? false) {
258+
List<String> argumentsList = firstArgument!.split(",");
259+
for (var argument in argumentsList) {
260+
// create stateful widget for each argument
261+
String widgetName =
262+
argument.snakeCase.replaceAll(RegExp(r'(_?widget)'), "");
251263

252-
ReCase classReCase = ReCase(widgetName);
264+
ReCase classReCase = ReCase(widgetName);
253265

254-
String stubStatefulWidget = widgetStatefulStub(classReCase);
255-
await MetroService.makeStatefulWidget(
256-
classReCase.snakeCase, stubStatefulWidget,
257-
forceCreate: hasForceFlag ?? false);
266+
String stubStatefulWidget = widgetStatefulStub(classReCase);
267+
await MetroService.makeStatefulWidget(
268+
classReCase.snakeCase, stubStatefulWidget,
269+
forceCreate: hasForceFlag ?? false);
270+
}
271+
} else {
272+
// create stateful widget for the first argument
273+
String widgetName =
274+
firstArgument!.snakeCase.replaceAll(RegExp(r'(_?widget)'), "");
275+
276+
ReCase classReCase = ReCase(widgetName);
277+
278+
String stubStatefulWidget = widgetStatefulStub(classReCase);
279+
await MetroService.makeStatefulWidget(
280+
classReCase.snakeCase, stubStatefulWidget,
281+
forceCreate: hasForceFlag ?? false);
282+
}
283+
}
284+
285+
/// Creates a Journey Widget file for Nylo projects
286+
/// E.g. run: `dart run nylo_framework:main make:journey_widget welcome_tab,users_dob,users_info --parent=Onboarding`
287+
_makeJourneyWidget(List<String> arguments) async {
288+
parser.addFlag(helpFlag,
289+
abbr: 'h',
290+
help: 'e.g. make:journey_widget welcome_tab,users_dob,users_info',
291+
negatable: false);
292+
parser.addFlag(forceFlag,
293+
abbr: 'f',
294+
help: 'Creates a new journey widget even if it already exists.',
295+
negatable: false);
296+
parser.addOption(parentOption,
297+
abbr: 'p', help: 'The parent navigation hub for the journey widget.');
298+
299+
final ArgResults argResults = parser.parse(arguments);
300+
301+
bool? hasForceFlag = argResults[forceFlag];
302+
String? parentNavigationHub = argResults[parentOption];
303+
304+
if (parentNavigationHub == null) {
305+
MetroConsole.writeInRed(
306+
"You must provide a parent navigation hub for the journey widget.\ne.g. make:journey_widget welcome_tab --parent=Onboarding");
307+
exit(1);
308+
}
309+
310+
ReCase parentReCase = ReCase(parentNavigationHub);
311+
// remove NavigationHub if it exists
312+
if (parentReCase.snakeCase.contains("navigation_hub")) {
313+
parentReCase = ReCase(
314+
parentReCase.snakeCase.replaceAll(RegExp(r'(_?navigation_hub)'), ""));
315+
}
316+
317+
MetroService.hasHelpFlag(argResults[helpFlag], parser.usage);
318+
319+
MetroService.checkArguments(arguments,
320+
'You are missing the \'name\' of the journey widget that you want to create.\ne.g. make:journey_widget my_new_widget');
321+
322+
final String? firstArgument = argResults.arguments.first;
323+
if (firstArgument?.contains(",") ?? false) {
324+
List<String> argumentsList = firstArgument!.split(",");
325+
for (var argument in argumentsList) {
326+
// create journey widget for each argument
327+
String widgetName =
328+
argument.snakeCase.replaceAll(RegExp(r'(_?widget)'), "");
329+
330+
ReCase classReCase = ReCase(widgetName);
331+
332+
String stubStatefulWidget = navigationTabJourneyStateStub(classReCase,
333+
parentNavigationHub: parentReCase);
334+
await MetroService.makeJourneyWidget(
335+
classReCase.snakeCase, stubStatefulWidget,
336+
forceCreate: hasForceFlag ?? false);
337+
}
338+
} else {
339+
// create journey widget for the first argument
340+
String widgetName =
341+
firstArgument!.snakeCase.replaceAll(RegExp(r'(_?widget)'), "");
342+
343+
ReCase classReCase = ReCase(widgetName);
344+
345+
String stubStatefulWidget = navigationTabJourneyStateStub(classReCase,
346+
parentNavigationHub: parentReCase);
347+
await MetroService.makeJourneyWidget(
348+
classReCase.snakeCase, stubStatefulWidget,
349+
forceCreate: hasForceFlag ?? false);
350+
}
258351
}
259352

260353
/// Creates a State Managed Widget file for Nylo projects
@@ -338,14 +431,33 @@ _makeStatelessWidget(List<String> arguments) async {
338431
MetroService.checkArguments(arguments,
339432
'You are missing the \'name\' of the widget that you want to create.\ne.g. make:stateless_widget my_new_widget');
340433

341-
String widgetName = argResults.arguments.first.snakeCase
342-
.replaceAll(RegExp(r'(_?widget)'), "");
343-
ReCase classReCase = ReCase(widgetName);
434+
final String? firstArgument = argResults.arguments.first;
435+
if (firstArgument?.contains(",") ?? false) {
436+
List<String> argumentsList = firstArgument!.split(",");
437+
for (var argument in argumentsList) {
438+
// create stateless widget for each argument
439+
String widgetName =
440+
argument.snakeCase.replaceAll(RegExp(r'(_?widget)'), "");
344441

345-
String stubStatelessWidget = widgetStatelessStub(classReCase);
346-
await MetroService.makeStatelessWidget(
347-
classReCase.snakeCase, stubStatelessWidget,
348-
forceCreate: hasForceFlag ?? false);
442+
ReCase classReCase = ReCase(widgetName);
443+
444+
String stubStatelessWidget = widgetStatelessStub(classReCase);
445+
await MetroService.makeStatelessWidget(
446+
classReCase.snakeCase, stubStatelessWidget,
447+
forceCreate: hasForceFlag ?? false);
448+
}
449+
} else {
450+
// create stateless widget for the first argument
451+
String widgetName =
452+
firstArgument!.snakeCase.replaceAll(RegExp(r'(_?widget)'), "");
453+
454+
ReCase classReCase = ReCase(widgetName);
455+
456+
String stubStatelessWidget = widgetStatelessStub(classReCase);
457+
await MetroService.makeStatelessWidget(
458+
classReCase.snakeCase, stubStatelessWidget,
459+
forceCreate: hasForceFlag ?? false);
460+
}
349461
}
350462

351463
/// Creates a Route Guard for Nylo projects

lib/metro/stubs/navigation_hub_stub.dart

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class _${rc.pascalCase}NavigationHubState extends NavigationHub<${rc.pascalCase}
2222
/// Layouts:
2323
/// - [NavigationHubLayout.bottomNav] Bottom navigation
2424
/// - [NavigationHubLayout.topNav] Top navigation
25+
/// - [NavigationHubLayout.journey] Journey navigation
2526
NavigationHubLayout? layout = NavigationHubLayout.bottomNav(
2627
// backgroundColor: Colors.white,
2728
);
@@ -32,19 +33,22 @@ class _${rc.pascalCase}NavigationHubState extends NavigationHub<${rc.pascalCase}
3233
3334
/// Navigation pages
3435
_${rc.pascalCase}NavigationHubState() : super(() async {
36+
/// * Creating Navigation Tabs
37+
/// [Navigation Tabs] 'dart run nylo_framework:main make:stateful_widget home_tab,settings_tab'
38+
/// [Journey States] 'dart run nylo_framework:main make:journey_widget welcome_tab,users_dob,users_info --parent=${rc.pascalCase}'
3539
return {
36-
0: NavigationTab(
37-
title: "Home",
38-
// page: HomeTab(), // create using: 'dart run nylo_framework:main make:stateful_widget home_tab'
39-
icon: Icon(Icons.home),
40-
activeIcon: Icon(Icons.home),
41-
),
42-
1: NavigationTab(
43-
title: "Settings",
44-
// page: SettingsTab(), // create using: 'dart run nylo_framework:main make:stateful_widget settings_tab'
45-
icon: Icon(Icons.settings),
46-
activeIcon: Icon(Icons.settings),
47-
),
40+
// 0: NavigationTab(
41+
// title: "Home",
42+
// // page: HomeTab(),
43+
// icon: Icon(Icons.home),
44+
// activeIcon: Icon(Icons.home),
45+
// ),
46+
// 1: NavigationTab(
47+
// title: "Settings",
48+
// // page: SettingsTab(),
49+
// icon: Icon(Icons.settings),
50+
// activeIcon: Icon(Icons.settings),
51+
// ),
4852
};
4953
});
5054
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import 'package:recase/recase.dart';
2+
3+
/// This stub is used to create a navigation tab Journey State widget
4+
String navigationTabJourneyStateStub(ReCase rc,
5+
{required ReCase parentNavigationHub}) =>
6+
'''
7+
import 'package:flutter/material.dart';
8+
import '/resources/pages/${parentNavigationHub.snakeCase}_navigation_hub.dart';
9+
import '/resources/widgets/buttons/buttons.dart';
10+
import 'package:nylo_framework/nylo_framework.dart';
11+
12+
class ${rc.pascalCase} extends StatefulWidget {
13+
const ${rc.pascalCase}({super.key});
14+
15+
@override
16+
createState() => _${rc.pascalCase}State();
17+
}
18+
19+
class _${rc.pascalCase}State extends JourneyState<${rc.pascalCase}> {
20+
_${rc.pascalCase}State() : super(
21+
navigationHubState: ${parentNavigationHub.pascalCase}NavigationHub.path.stateName());
22+
23+
@override
24+
get init => () {
25+
// Your initialization logic here
26+
};
27+
28+
@override
29+
Widget view(BuildContext context) {
30+
return buildJourneyContent(
31+
content: Column(
32+
mainAxisAlignment: MainAxisAlignment.center,
33+
children: [
34+
Text('${rc.pascalCase}', style: Theme.of(context).textTheme.headlineMedium),
35+
const SizedBox(height: 20),
36+
Text('This onboarding journey will help you get started.'),
37+
],
38+
),
39+
nextButton: Button.primary(
40+
text: isLastStep ? "Get Started" : "Continue",
41+
onPressed: onNextPressed,
42+
),
43+
backButton: isFirstStep ? null : Button.textOnly(
44+
text: "Back",
45+
textColor: Colors.black87,
46+
onPressed: onBackPressed,
47+
),
48+
);
49+
}
50+
51+
/// Check if the journey can continue to the next step
52+
/// Override this method to add validation logic
53+
@override
54+
Future<bool> canContinue() async {
55+
// Perform your validation logic here
56+
// Return true if the journey can continue, false otherwise
57+
return true;
58+
}
59+
60+
/// Called when unable to continue (canContinue returns false)
61+
/// Override this method to handle validation failures
62+
@override
63+
Future<void> onCannotContinue() async {
64+
showToastSorry(description: "You cannot continue");
65+
}
66+
67+
/// Called before navigating to the next step
68+
/// Override this method to perform actions before continuing
69+
@override
70+
Future<void> onBeforeNext() async {
71+
// E.g. save data to session
72+
// session('onboarding', {
73+
// 'name': 'Anthony Gordon',
74+
// 'occupation': 'Software Engineer',
75+
// });
76+
//
77+
// final sessionData = session('onboarding').data(); // {'name': 'Anthony Gordon', 'occupation': 'Software Engineer'}
78+
// printInfo(sessionData);
79+
80+
// access the session data from other NavigationTabs
81+
}
82+
83+
/// Called after navigating to the next step
84+
/// Override this method to perform actions after continuing
85+
@override
86+
Future<void> onAfterNext() async {
87+
print('Navigated to the next step');
88+
}
89+
90+
/// Called when the journey is complete (at the last step)
91+
/// Override this method to perform completion actions
92+
@override
93+
Future<void> onComplete() async {}
94+
}
95+
''';

lib/nylo_framework.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,4 @@ export 'package:date_field/date_field.dart';
2929
export 'package:dio/dio.dart';
3030

3131
/// Nylo version
32-
const String nyloVersion = 'v6.7.6';
32+
const String nyloVersion = 'v6.8.0';

pubspec.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,10 @@ packages:
210210
dependency: transitive
211211
description:
212212
name: flutter_local_notifications
213-
sha256: d59eeafd6df92174b1d5f68fc9d66634c97ce2e7cfe2293476236547bb19bbbd
213+
sha256: "33b3e0269ae9d51669957a923f2376bee96299b09915d856395af8c4238aebfa"
214214
url: "https://pub.dev"
215215
source: hosted
216-
version: "19.0.0"
216+
version: "19.1.0"
217217
flutter_local_notifications_linux:
218218
dependency: transitive
219219
description:
@@ -441,10 +441,10 @@ packages:
441441
dependency: "direct main"
442442
description:
443443
name: nylo_support
444-
sha256: b7f3ca15e933dcec3d43ea1ae7d4bf332531e66f21291cce1ddad9da58835e8b
444+
sha256: "1c77a427ff4872d9882646a0b83b4a63ce1c864673ea18bd17958aeb37ab0532"
445445
url: "https://pub.dev"
446446
source: hosted
447-
version: "6.25.1"
447+
version: "6.26.0"
448448
path:
449449
dependency: transitive
450450
description:

0 commit comments

Comments
 (0)