Skip to content

Commit

Permalink
Add better demos
Browse files Browse the repository at this point in the history
  • Loading branch information
esDotDev committed Apr 15, 2021
1 parent 63e4f8e commit 44a59b8
Show file tree
Hide file tree
Showing 17 changed files with 1,021 additions and 149 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
## 0.0.1
## 0.0.3
* Better demos

## 0.0.2+3
* First release
264 changes: 199 additions & 65 deletions README.md

Large diffs are not rendered by default.

50 changes: 50 additions & 0 deletions example/lib/advanced_tabs_buttons.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:nav_stack/nav_stack.dart';

import 'advanced_tabs_demo_pages.dart';

/// This btn reads the global path to figure out if it should be "selected",
/// On pressed, it updates the global path with it's .target value.
/// It wraps Expanded() and is mean to be placed in a Row() widget.
/// All Scaffolds use this button in their tab menus.
class NavBtn extends StatelessWidget {
final String target;
final String? selectionAlias;
final String label;

const NavBtn(this.label, {Key? key, required this.target, this.selectionAlias}) : super(key: key);
@override
Widget build(BuildContext context) {
// We're selected if the current path matches our target, or our alias
bool isSelected = NavStack.of(context).path.contains(selectionAlias ?? target);
TextStyle textStyle = TextStyle(fontSize: 22, color: isSelected ? Colors.black : Colors.blue);
return Expanded(
child: OutlinedButton(
onPressed: () => NavStack.of(context).path = target,
child: Padding(padding: EdgeInsets.symmetric(vertical: 10), child: Text(label, style: textStyle))),
);
}
}

/// This buttons goes back one level in the nav-stack, and auto-hides itself on Web
class BackBtn extends StatelessWidget {
Widget build(BuildContext context) =>
kIsWeb ? SizedBox() : OutlinedButton(onPressed: NavStack.of(context).goBack, child: Text("<< Back"));
}

/// This buttons opens the details view and passes it as a parameter like `/details/92`
class OpenDetailsBtn extends StatelessWidget {
const OpenDetailsBtn({Key? key, required this.child, required this.itemId}) : super(key: key);
final Widget child;
final String itemId;

Widget build(BuildContext context) =>
TextButton(onPressed: () => NavStack.of(context).path = "${DetailsPage.path}$itemId", child: child);
}

/// This buttons pops all details views, allowing us to jump back to whichever root opened them in the first place
class CloseDetailsBtn extends StatelessWidget {
Widget build(BuildContext context) =>
CloseButton(onPressed: () => NavStack.of(context).popMatching(DetailsPage.path));
}
62 changes: 62 additions & 0 deletions example/lib/advanced_tabs_demo.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
import 'package:nav_stack/nav_stack.dart';

import 'advanced_tabs_demo_pages.dart';
import 'advanced_tabs_demo_scaffold.dart';

class AdvancedTabsDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return NavStack(
initialPath: AppPaths.tabsCategory + HomePage.path,
stackBuilder: (_, controller) {
void _handleComposePressed() => controller.path = ComposePage.path;
return PathStack(
routes: {
[AppPaths.tabsCategory]: PathStack(
// Main scaffold is wrapped here
scaffoldBuilder: (_, stack) => MainScaffold(
child: stack,
currentPath: NavStack.of(context).path,
// Goto /compose when this is pressed
onComposePressed: _handleComposePressed,
),
transitionBuilder: (_, stack, animation) => FadeTransition(opacity: animation, child: stack),
routes: {
// Home
[HomePage.path]: HomePage("").buildStackRoute(),
// Settings
[AppPaths.settings]: PathStack(
// Settings scaffold is wrapped here
scaffoldBuilder: (_, child) => SettingsScaffold(child: child),
routes: {
[ProfileSettings.path]: ProfileSettings("").buildStackRoute(),
[AlertSettings.path]: AlertSettings("").buildStackRoute(),
[BillingSettings.path]: BillingSettings("").buildStackRoute(maintainState: false),
},
).buildStackRoute(),
// Inbox
[AppPaths.inbox]: PathStack(
scaffoldBuilder: (_, child) => InboxScaffold(child: child),
routes: {
[InboxPage.friendsPath]: InboxPage(InboxType.friends).buildStackRoute(),
[InboxPage.unreadPath]: InboxPage(InboxType.unread).buildStackRoute(),
[InboxPage.archivedPath]: InboxPage(InboxType.archived).buildStackRoute(),
},
).buildStackRoute(),
},
).buildStackRoute(),

/// Non stateful full-screen route
[ComposePage.path]: ComposePage().buildStackRoute(maintainState: false),

/// Inject itemId param into detailsView
[DetailsPage.path + ":id"]: StackRouteBuilder(
maintainState: false,
builder: (_, args) => DetailsPage(itemId: args["id"]),
)
},
);
});
}
}
209 changes: 209 additions & 0 deletions example/lib/advanced_tabs_demo_pages.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import 'dart:math';

import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:nav_stack/nav_stack.dart';

import 'advanced_tabs_buttons.dart';

/// Sample Content Pages
class AppPaths {
static const String tabsCategory = "tabs/";
static const String settings = "settings/";
static const String inbox = "inbox/";
}

class SomeStatefulPage extends StatefulWidget {
SomeStatefulPage(String title, {Key? key}) : super(key: key) {
this.title = title;
print("${this} TITLE = $title");
}
late final String title;

@override
_SomeStatefulPageState createState() => _SomeStatefulPageState();
}

class _SomeStatefulPageState extends State<SomeStatefulPage> {
String _filter = "";
List<String>? items;
TextEditingController txtController = TextEditingController();
@override
void initState() {
super.initState();
Future.delayed(Duration(seconds: 1), () {
if (mounted == false) return;
setState(() => items = List.generate(100, (index) => "Item: $index"));
});
}

@override
Widget build(BuildContext context) {
if (items == null) return Center(child: CircularProgressIndicator());
// Filter Items
final filteredItems = List.from(items!)
..removeWhere((name) => name.toLowerCase().contains(_filter.toLowerCase()) == false);
return Column(
children: [
Row(
children: [
Text(widget.title, style: TextStyle(fontSize: 22)),
Spacer(),
Text("Search Filter:", style: TextStyle(fontSize: 16)),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
onChanged: (v) {
setState(() => _filter = v);
},
decoration: InputDecoration(border: OutlineInputBorder()),
),
)),
],
),
Expanded(
child: ListView.builder(
itemCount: filteredItems.length,
itemBuilder: (_, index) {
return OpenDetailsBtn(
itemId: "$index",
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(8.0),
color: (index % 2 == 0 ? Colors.grey : Colors.white).withOpacity(.1),
child: Row(
children: [
SizedBox(
width: 100,
height: 100,
child: CachedNetworkImage(
imageUrl: "https://source.unsplash.com/random/50x50?id=${widget.title}$index")),
Text(filteredItems[index]),
],
),
),
);
})),
],
);
}
}

class HomePage extends SomeStatefulPage {
static const String path = "home";
HomePage(String suffix) : super("Home Page, $suffix");
}

/// Settings
class ProfileSettings extends SomeStatefulPage {
static const String path = "profile";
ProfileSettings(String suffix) : super("Profile, $suffix");
}

class AlertSettings extends SomeStatefulPage {
static const String path = "alerts";
AlertSettings(String suffix) : super("Alerts, $suffix");
}

class BillingSettings extends SomeStatefulPage {
static const String path = "billing";
BillingSettings(String suffix) : super("Billing, $suffix");
}

/// Messages
enum InboxType { friends, unread, archived }

class InboxPage extends SomeStatefulPage {
static const String friendsPath = "friends/";
static const String unreadPath = "unread/";
static const String archivedPath = "archived/";

final InboxType pageType;
InboxPage(this.pageType) : super("$pageType");
}

/// Compose
class ComposePage extends StatefulWidget {
static const String path = "compose";
@override
_ComposePageState createState() => _ComposePageState();
}

class _ComposePageState extends State<ComposePage> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
BackBtn(),
Expanded(
child: Column(
children: [
Spacer(),
Text("New Message:", style: TextStyle(fontSize: 32)),
TextField(
maxLines: 10,
decoration: InputDecoration(border: OutlineInputBorder(borderSide: BorderSide(color: Colors.black)))),
Spacer(flex: 3),
],
))
],
),
);
}
}

/// Details
class DetailsPage extends StatefulWidget {
static const String path = "details/";

DetailsPage({required this.itemId}) : super();
final String? itemId;
@override
_DetailsPageState createState() => _DetailsPageState();
}

class _DetailsPageState extends State<DetailsPage> {
// Simulates a list of items loaded from the database.
// Normally this would be driven by some API call or model that exists above this view.
// The point of this is to just show how we can create a history-stack from a fullscreen view.
List<String> items = List.generate(100, (index) => "$index");
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Row(children: [
BackBtn(),
Spacer(),
CloseDetailsBtn(),
]),
Text("${widget.itemId}", style: TextStyle(fontSize: 32)),
Row(
children: [
OpenDetailsBtn(itemId: getId(-1), child: Text("<< prev item")),
Spacer(),
OpenDetailsBtn(itemId: getId(1), child: Text("next item >>")),
],
),
],
),
);
}

String getId(int dir) {
int currentIndex = items.indexOf(widget.itemId!);
currentIndex += dir;
if (currentIndex <= 0) {
currentIndex = items.length - 1;
} else if (currentIndex > items.length - 1) {
currentIndex = 0;
}
return items[currentIndex];
}
}

0 comments on commit 44a59b8

Please sign in to comment.