adding packages
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
# Screenshots
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
# Example
|
||||
|
||||
``` dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:responsive_scaffold/responsive_scaffold.dart';
|
||||
|
||||
class MultiColumnNavigationExample extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ThreeColumnNavigation(
|
||||
title: Text('Mailboxes'),
|
||||
showDetailsArrows: true,
|
||||
backgroundColor: Colors.grey[100],
|
||||
bottomAppBar: BottomAppBar(
|
||||
elevation: 1,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.filter_list,
|
||||
color: Colors.transparent,
|
||||
),
|
||||
onPressed: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
sections: [
|
||||
MainSection(
|
||||
label: Text('All Inboxes'),
|
||||
icon: Icon(Icons.mail),
|
||||
itemCount: 100,
|
||||
itemBuilder: (context, index, selected) {
|
||||
return ListTile(
|
||||
leading: CircleAvatar(
|
||||
child: Text(index.toString()),
|
||||
),
|
||||
selected: selected,
|
||||
title: Text('Primary Information'),
|
||||
subtitle: Text('Here are some details about the item'),
|
||||
);
|
||||
},
|
||||
bottomAppBar: BottomAppBar(
|
||||
elevation: 1,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.filter_list),
|
||||
onPressed: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
getDetails: (context, index) {
|
||||
return DetailsWidget(
|
||||
title: Text('Details'),
|
||||
child: Center(
|
||||
child: Text(
|
||||
index.toString(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
MainSection(
|
||||
label: Text('Sent Mail'),
|
||||
icon: Icon(Icons.send),
|
||||
itemCount: 100,
|
||||
itemBuilder: (context, index, selected) {
|
||||
return ListTile(
|
||||
leading: CircleAvatar(
|
||||
child: Text(index.toString()),
|
||||
),
|
||||
selected: selected,
|
||||
title: Text('Secondary Information'),
|
||||
subtitle: Text('Here are some details about the item'),
|
||||
);
|
||||
},
|
||||
getDetails: (context, index) {
|
||||
return DetailsWidget(
|
||||
title: Text('Details'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.share),
|
||||
onPressed: () {},
|
||||
),
|
||||
],
|
||||
child: Center(
|
||||
child: Text(
|
||||
index.toString(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
@@ -0,0 +1,66 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../three_column_navigation.dart';
|
||||
|
||||
class DetailsView extends StatelessWidget {
|
||||
const DetailsView({
|
||||
Key key,
|
||||
@required int listIndex,
|
||||
@required DetailsWidget details,
|
||||
this.previous,
|
||||
this.next,
|
||||
this.isLast = false,
|
||||
this.isFirst = false,
|
||||
this.automaticallyImplyLeading = true,
|
||||
this.showDetailsArrows = false,
|
||||
this.scaffoldKey,
|
||||
}) : _listIndex = listIndex,
|
||||
_details = details,
|
||||
super(key: key);
|
||||
|
||||
final int _listIndex;
|
||||
final DetailsWidget _details;
|
||||
final VoidCallback previous, next;
|
||||
final bool showDetailsArrows;
|
||||
final bool isLast, isFirst;
|
||||
final bool automaticallyImplyLeading;
|
||||
final GlobalKey<ScaffoldState> scaffoldKey;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
key: scaffoldKey,
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: automaticallyImplyLeading,
|
||||
title: !showDetailsArrows
|
||||
? _details.title
|
||||
: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.arrow_upward),
|
||||
onPressed: isFirst ? null : previous,
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.arrow_downward),
|
||||
onPressed: isLast ? null : next,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (_details?.title != null)
|
||||
Expanded(
|
||||
child: Center(child: _details.title),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: _details?.actions,
|
||||
),
|
||||
body: _details.child,
|
||||
bottomNavigationBar: _details?.bottomAppBar,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export 'details.dart';
|
||||
export 'section_list.dart';
|
||||
export 'sections.dart';
|
||||
@@ -0,0 +1,41 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:scroll_to_index/scroll_to_index.dart';
|
||||
|
||||
import '../three_column_navigation.dart';
|
||||
|
||||
class SectionList extends StatelessWidget {
|
||||
const SectionList({
|
||||
Key key,
|
||||
@required this.controller,
|
||||
@required MainSection section,
|
||||
@required int listIndex,
|
||||
@required this.listTap,
|
||||
}) : _section = section,
|
||||
_listIndex = listIndex,
|
||||
super(key: key);
|
||||
|
||||
final AutoScrollController controller;
|
||||
final MainSection _section;
|
||||
final int _listIndex;
|
||||
final ValueChanged<int> listTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.builder(
|
||||
controller: controller,
|
||||
itemBuilder: (context, index) {
|
||||
final _item = _section.itemBuilder(context, index, _listIndex == index);
|
||||
return AutoScrollTag(
|
||||
controller: controller,
|
||||
key: ValueKey(index),
|
||||
index: index,
|
||||
child: GestureDetector(
|
||||
child: _item,
|
||||
onTap: () => listTap(index),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: _section.itemCount,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../three_column_navigation.dart';
|
||||
|
||||
class SectionsList extends StatelessWidget {
|
||||
const SectionsList({
|
||||
Key key,
|
||||
@required this.sectionTap,
|
||||
@required int sectionIndex,
|
||||
@required this.sections,
|
||||
}) : _sectionIndex = sectionIndex,
|
||||
super(key: key);
|
||||
|
||||
final int _sectionIndex;
|
||||
final ValueChanged<int> sectionTap;
|
||||
final List<MainSection> sections;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.builder(
|
||||
itemBuilder: (context, index) {
|
||||
final _item = sections[index];
|
||||
return ListTile(
|
||||
leading: _item.icon,
|
||||
title: _item.label,
|
||||
selected: index == _sectionIndex,
|
||||
onTap: () => sectionTap(index),
|
||||
);
|
||||
},
|
||||
itemCount: sections.length,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:scroll_to_index/scroll_to_index.dart';
|
||||
|
||||
import 'common/index.dart';
|
||||
|
||||
class ThreeColumnNavigation extends StatefulWidget {
|
||||
ThreeColumnNavigation({
|
||||
@required this.sections,
|
||||
this.showDetailsArrows = true,
|
||||
this.expandedIconData = Icons.fullscreen_exit,
|
||||
this.collapsedIconData = Icons.fullscreen,
|
||||
this.initiallyExpanded = true,
|
||||
this.bottomAppBar,
|
||||
this.backgroundColor,
|
||||
this.title,
|
||||
});
|
||||
|
||||
final Color backgroundColor;
|
||||
final Widget bottomAppBar;
|
||||
final IconData expandedIconData, collapsedIconData;
|
||||
final bool initiallyExpanded;
|
||||
List<MainSection> sections;
|
||||
final bool showDetailsArrows;
|
||||
final Text title;
|
||||
|
||||
@override
|
||||
_ThreeColumnNavigationState createState() => _ThreeColumnNavigationState();
|
||||
}
|
||||
|
||||
class _ThreeColumnNavigationState extends State<ThreeColumnNavigation> {
|
||||
AutoScrollController controller;
|
||||
|
||||
bool _expanded = true;
|
||||
int _listIndex = 0;
|
||||
final _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
int _sectionIndex = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
if (!widget.initiallyExpanded) if (mounted)
|
||||
setState(() {
|
||||
_expanded = false;
|
||||
});
|
||||
|
||||
_setUpController(true);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void _setUpController(bool init) {
|
||||
controller = AutoScrollController(
|
||||
viewportBoundaryGetter: () =>
|
||||
Rect.fromLTRB(0, 0, 0, MediaQuery.of(context).padding.bottom),
|
||||
axis: Axis.vertical,
|
||||
);
|
||||
}
|
||||
|
||||
Future _scrollToIndex(int value,
|
||||
{AutoScrollPosition position = AutoScrollPosition.middle}) async {
|
||||
try {
|
||||
controller.scrollToIndex(
|
||||
value,
|
||||
preferPosition: position,
|
||||
);
|
||||
// controller.highlight(value);
|
||||
} on Exception catch (e) {
|
||||
print('Could not scroll to index: $value => $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
if (constraints.maxWidth > 720) {
|
||||
final _showMenu = constraints.maxWidth < 1100;
|
||||
if (_showMenu) {
|
||||
_expanded = false;
|
||||
}
|
||||
return Material(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
if (_expanded)
|
||||
Container(
|
||||
width: 300,
|
||||
child: Scaffold(
|
||||
backgroundColor: widget?.backgroundColor,
|
||||
appBar: AppBar(
|
||||
title: widget?.title,
|
||||
leading: IconButton(
|
||||
icon: Icon(widget.expandedIconData),
|
||||
onPressed: () {
|
||||
if (mounted)
|
||||
setState(() {
|
||||
_expanded = false;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
body: SectionsList(
|
||||
sections: widget.sections,
|
||||
sectionIndex: _sectionIndex,
|
||||
sectionTap: (index) {
|
||||
if (_sectionIndex != index) {
|
||||
if (mounted)
|
||||
setState(() {
|
||||
_sectionIndex = index;
|
||||
});
|
||||
_setUpController(false);
|
||||
}
|
||||
},
|
||||
),
|
||||
bottomNavigationBar: widget?.bottomAppBar,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 400,
|
||||
child: Scaffold(
|
||||
drawerScrimColor: Colors.transparent,
|
||||
drawer: _showMenu
|
||||
? SectionsDrawer(
|
||||
sections: widget.sections,
|
||||
sectionIndex: _sectionIndex,
|
||||
sectionChanged: (context, index) {
|
||||
if (_sectionIndex != index) {
|
||||
if (mounted)
|
||||
setState(() {
|
||||
_sectionIndex = index;
|
||||
});
|
||||
_setUpController(false);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
)
|
||||
: null,
|
||||
appBar: AppBar(
|
||||
leading: _showMenu
|
||||
? MenuButton(scaffoldKey: _scaffoldKey)
|
||||
: !_expanded
|
||||
? IconButton(
|
||||
icon: Icon(widget.collapsedIconData),
|
||||
onPressed: () {
|
||||
if (mounted)
|
||||
setState(() {
|
||||
_expanded = true;
|
||||
});
|
||||
},
|
||||
)
|
||||
: null,
|
||||
title: widget.sections[_sectionIndex].label,
|
||||
),
|
||||
body: SectionList(
|
||||
controller: controller,
|
||||
section: widget.sections[_sectionIndex],
|
||||
listIndex: _listIndex,
|
||||
listTap: (index) {
|
||||
if (_listIndex != index) {
|
||||
if (mounted)
|
||||
setState(() {
|
||||
_listIndex = index;
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
bottomNavigationBar:
|
||||
widget.sections[_sectionIndex]?.bottomAppBar,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
DetailsView(
|
||||
scaffoldKey: _scaffoldKey,
|
||||
automaticallyImplyLeading: false,
|
||||
isFirst: _listIndex == 0,
|
||||
isLast: widget.sections[_sectionIndex].itemCount ==
|
||||
_listIndex + 1,
|
||||
listIndex: _listIndex,
|
||||
details: widget.sections[_sectionIndex]
|
||||
.getDetails(context, _listIndex),
|
||||
showDetailsArrows: widget.showDetailsArrows,
|
||||
previous: () {
|
||||
if (mounted)
|
||||
setState(() {
|
||||
_listIndex--;
|
||||
});
|
||||
_scrollToIndex(_listIndex);
|
||||
},
|
||||
next: () {
|
||||
if (mounted)
|
||||
setState(() {
|
||||
_listIndex++;
|
||||
});
|
||||
_scrollToIndex(_listIndex);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
return Scaffold(
|
||||
drawer: SectionsDrawer(
|
||||
sections: widget.sections,
|
||||
sectionIndex: _sectionIndex,
|
||||
sectionChanged: (context, index) {
|
||||
if (_sectionIndex != index) {
|
||||
if (mounted)
|
||||
setState(() {
|
||||
_sectionIndex = index;
|
||||
});
|
||||
|
||||
_setUpController(false);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
),
|
||||
appBar: AppBar(
|
||||
title: widget.sections[_sectionIndex]?.label,
|
||||
),
|
||||
body: SectionList(
|
||||
controller: controller,
|
||||
section: widget.sections[_sectionIndex],
|
||||
listIndex: _listIndex,
|
||||
listTap: (index) {
|
||||
if (_listIndex != index) {
|
||||
if (mounted)
|
||||
setState(() {
|
||||
_listIndex = index;
|
||||
});
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (_) {
|
||||
final _details = widget.sections[_sectionIndex]
|
||||
.getDetails(context, _listIndex);
|
||||
return DetailsView(
|
||||
isFirst: _listIndex == 0,
|
||||
isLast: widget.sections.isNotEmpty &&
|
||||
_listIndex == widget.sections.length - 1,
|
||||
listIndex: _listIndex,
|
||||
details: _details,
|
||||
showDetailsArrows: false,
|
||||
);
|
||||
},
|
||||
));
|
||||
}
|
||||
},
|
||||
),
|
||||
bottomNavigationBar: widget.sections[_sectionIndex]?.bottomAppBar,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MenuButton extends StatelessWidget {
|
||||
const MenuButton({
|
||||
Key key,
|
||||
@required GlobalKey<ScaffoldState> scaffoldKey,
|
||||
}) : _scaffoldKey = scaffoldKey,
|
||||
super(key: key);
|
||||
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IconButton(
|
||||
icon: Icon(Icons.menu),
|
||||
onPressed: () {
|
||||
Scaffold.of(context).openDrawer();
|
||||
// _scaffoldKey.currentState.showBodyScrim(true, 0.5);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SectionsDrawer extends StatelessWidget {
|
||||
const SectionsDrawer({
|
||||
Key key,
|
||||
@required int sectionIndex,
|
||||
@required this.sectionChanged,
|
||||
@required this.sections,
|
||||
}) : _sectionIndex = sectionIndex,
|
||||
super(key: key);
|
||||
|
||||
final List<MainSection> sections;
|
||||
|
||||
final int _sectionIndex;
|
||||
|
||||
final Function(BuildContext, int) sectionChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Drawer(
|
||||
child: SectionsList(
|
||||
sections: sections,
|
||||
sectionIndex: _sectionIndex,
|
||||
sectionTap: (index) => sectionChanged(context, index),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MainSection {
|
||||
const MainSection({
|
||||
@required this.itemBuilder,
|
||||
@required this.itemCount,
|
||||
@required this.getDetails,
|
||||
@required this.icon,
|
||||
@required this.label,
|
||||
this.bottomAppBar,
|
||||
});
|
||||
|
||||
final Widget bottomAppBar;
|
||||
final Icon icon;
|
||||
final int itemCount;
|
||||
final Text label;
|
||||
|
||||
final Widget Function(BuildContext context, int index, bool selected)
|
||||
itemBuilder;
|
||||
|
||||
final DetailsWidget Function(BuildContext context, int index) getDetails;
|
||||
}
|
||||
|
||||
class DetailsWidget {
|
||||
const DetailsWidget({
|
||||
@required this.child,
|
||||
this.actions,
|
||||
this.title,
|
||||
this.bottomAppBar,
|
||||
});
|
||||
|
||||
final List<Widget> actions;
|
||||
final Widget bottomAppBar;
|
||||
final Widget child;
|
||||
final Text title;
|
||||
}
|
||||
Reference in New Issue
Block a user