Skip to content

Commit

Permalink
feat: Reordering items within the same level. #4
Browse files Browse the repository at this point in the history
  • Loading branch information
LuchoTurtle committed Apr 28, 2023
1 parent 62ef04c commit 35651d2
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 25 deletions.
14 changes: 14 additions & 0 deletions assets/menu_items.json
Original file line number Diff line number Diff line change
@@ -1,45 +1,59 @@
[
{
"id": 1,
"index_in_level": 0,
"title": "People",
"tiles": [
{
"id": 2,
"index_in_level": 0,
"title": "Online Now",
"tiles": [
{
"id": 3,
"index_in_level": 0,
"title": "Family",
"tiles": []
},
{
"id": 4,
"index_in_level": 1,
"title": "Friends",
"tiles": [
{
"id": 5,
"index_in_level": 0,
"title": "Sports Team",
"tiles": []
},
{
"id": 6,
"index_in_level": 1,
"title": "Gamerz",
"tiles": []
}
]
},
{
"id": 7,
"index_in_level": 2,
"title": "Work",
"tiles": []
}
]
},
{
"id": 8,
"index_in_level": 1,
"title": "Everyone",
"tiles": []
}
]
},
{
"id": 9,
"index_in_level": 1,
"title": "Potaro",
"tiles": []
}
]
95 changes: 74 additions & 21 deletions lib/menu.dart
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'pages.dart';
import 'tiles.dart';
import 'settings.dart';

const drawerMenuKey = Key("drawer_menu");
const todoTileKey = Key("todo_tile");
const tourTileKey = Key("tour_tile");
const settingsTileKey = Key("settings_tile");

const closeMenuKey = Key("close_key_icon");

class DrawerMenu extends StatelessWidget {
class DrawerMenu extends StatefulWidget {
const DrawerMenu({super.key});

Future<List<MenuItemInfo>> _loadMenuItems() async {
final String response = await rootBundle.loadString('assets/menu_items.json');
List<dynamic> data = await json.decode(response);
@override
State<DrawerMenu> createState() => _DrawerMenuState();
}

final List<MenuItemInfo> menuItems = data.map((obj) => MenuItemInfo.fromJson(obj)).toList();
class _DrawerMenuState extends State<DrawerMenu> with SettingsManagerMixin {
late Future<List<MenuItemInfo>> menuItems;

return menuItems;
@override
void initState() {
super.initState();
menuItems = loadMenuItems();
}


@override
Widget build(BuildContext context) {
return Scaffold(
Expand All @@ -50,18 +51,14 @@ class DrawerMenu extends StatelessWidget {
body: Container(
color: Colors.black,
child: FutureBuilder<List<MenuItemInfo>>(
future: _loadMenuItems(),
future: menuItems,
builder: (BuildContext context, AsyncSnapshot<List<MenuItemInfo>> snapshot) {
// If the data is correctly loaded
// If the data is correctly loaded,
// we render a `ReorderableListView` whose children are `MenuItem` tiles.
if (snapshot.hasData) {
return ListView(
key: todoTileKey,
padding: const EdgeInsets.only(top: 32),
children: snapshot.data!
.map(
(tile) => MenuItem(info: tile),
)
.toList());
List<MenuItemInfo> menuItemInfoList = snapshot.data!;

return DrawerMenuTilesList(menuItemInfoList: menuItemInfoList);
}

// While it's not loaded (error or waiting)
Expand All @@ -72,3 +69,59 @@ class DrawerMenu extends StatelessWidget {
);
}
}

// Widget with the list of Menu Item tiles
class DrawerMenuTilesList extends StatefulWidget {
final List<MenuItemInfo> menuItemInfoList;

const DrawerMenuTilesList({super.key, required this.menuItemInfoList});

@override
State<DrawerMenuTilesList> createState() => _DrawerMenuTilesListState();
}

class _DrawerMenuTilesListState extends State<DrawerMenuTilesList> {
late List<MenuItemInfo> menuItemInfoList;

@override
void initState() {
super.initState();
menuItemInfoList = widget.menuItemInfoList;
}

/// Callback function that reorders the tiles
void _reorderTiles(int oldIndex, int newIndex, List<MenuItemInfo> menuItemInfoList) {
// an adjustment is needed when moving the tile down the list
if (oldIndex < newIndex) {
newIndex--;
}

// get the tile we are moving
final tile = menuItemInfoList.removeAt(oldIndex);

// place the tile in the new position
menuItemInfoList.insert(newIndex, tile);

// Update state
setState(() {
menuItemInfoList = menuItemInfoList;
});

// TODO: update the JSON file (change index_at_level)
}

@override
Widget build(BuildContext context) {
return ReorderableListView(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: const EdgeInsets.only(top: 32),
onReorder: (oldIndex, newIndex) => _reorderTiles(oldIndex, newIndex, menuItemInfoList),
children: menuItemInfoList
.map(
(tile) => MenuItem(key: ValueKey(tile.id), info: tile),
)
.toList()
);
}
}
27 changes: 27 additions & 0 deletions lib/settings.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'dart:convert';

import 'package:flutter/services.dart';

import 'tiles.dart';

mixin SettingsManagerMixin {
final jsonFilePath = 'assets/menu_items.json';


/// Loads the MenuItemInfo list from the json file
Future<List<MenuItemInfo>> loadMenuItems() async {
// Get string from json
final String response = await rootBundle.loadString(jsonFilePath);
List<dynamic> data = await json.decode(response);

// Converting json to list of MenuItemInfo objects
final List<MenuItemInfo> menuItems = data.map((obj) => MenuItemInfo.fromJson(obj)).toList();

// Return the MenuItemInfo list
return menuItems;
}




}
54 changes: 50 additions & 4 deletions lib/tiles.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ import 'package:flutter/material.dart';

/// Class holding the information of the tile
class MenuItemInfo {
late int id;
late int indexInLevel;
late String title;
late List<MenuItemInfo> tiles;

MenuItemInfo({required this.title, this.tiles = const []});
MenuItemInfo({required this.id, required this.title, this.tiles = const []});

/// Converts `json` text to BasicTile
MenuItemInfo.fromJson(Map<String, dynamic> json) {
id = json['id'];
indexInLevel = json['index_in_level'];
title = json['title'];
if (json['tiles'] != null) {
tiles = [];
Expand All @@ -21,23 +25,54 @@ class MenuItemInfo {

/// Custom tile class that expands if there are child tiles or not.
class MenuItem extends StatefulWidget {
final Key key;
final MenuItemInfo info;
final double leftPadding;

const MenuItem({super.key, required this.info, this.leftPadding = 16});
const MenuItem({required this.key, required this.info, this.leftPadding = 16}) : super(key: key);

@override
State<MenuItem> createState() => _MenuItemState();
}

class _MenuItemState extends State<MenuItem> {
bool _expanded = false;

late List<MenuItemInfo> menuItemInfoList;

@override
void initState() {
super.initState();
menuItemInfoList = widget.info.tiles;
}

/// Callback function that reorders the tiles
void _reorderTiles(int oldIndex, int newIndex, List<MenuItemInfo> menuItemInfoList) {
// an adjustment is needed when moving the tile down the list
if (oldIndex < newIndex) {
newIndex--;
}

// get the tile we are moving
final tile = menuItemInfoList.removeAt(oldIndex);

// place the tile in the new position
menuItemInfoList.insert(newIndex, tile);

// Update state
setState(() {
menuItemInfoList = menuItemInfoList;
});

// TODO: update the JSON file (change index_at_level)
}

@override
Widget build(BuildContext context) {
// If the tile's children is empty, we render the leaf tile
if (widget.info.tiles.isEmpty) {
if (menuItemInfoList.isEmpty) {
return Container(
key: widget.key,
decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Colors.white))),
child: ListTile(
contentPadding: EdgeInsets.only(left: widget.leftPadding),
Expand All @@ -53,6 +88,10 @@ class _MenuItemState extends State<MenuItem> {
else {
return Container(
decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Colors.white))),

// Rendering `ExpansionTile` which expands to render the children.
// The children are rendered in a `ReorderableListView`
// so they can be reordered on the same level.
child: ExpansionTile(
tilePadding: EdgeInsets.only(left: widget.leftPadding),
title: Text(widget.info.title,
Expand All @@ -64,7 +103,14 @@ class _MenuItemState extends State<MenuItem> {
_expanded ? Icons.expand_less : Icons.arrow_drop_down,
color: Colors.white,
),
children: widget.info.tiles.map((tile) => MenuItem(info: tile, leftPadding: widget.leftPadding + 16)).toList(),
children: [
ReorderableListView(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
onReorder: (oldIndex, newIndex) => _reorderTiles(oldIndex, newIndex, menuItemInfoList),
children: menuItemInfoList.map((tile) => MenuItem(key: ValueKey(tile.id), info: tile, leftPadding: widget.leftPadding + 16)).toList(),
)
],
onExpansionChanged: (bool expanded) {
setState(() => _expanded = expanded);
},
Expand Down

0 comments on commit 35651d2

Please sign in to comment.