11import 'dart:io' ;
22
3+ import 'package:collection/collection.dart' ;
4+ import 'package:files/backend/folder_provider.dart' ;
35import 'package:files/backend/path_parts.dart' ;
6+ import 'package:files/backend/providers.dart' ;
47import 'package:files/backend/utils.dart' ;
58import 'package:flutter/material.dart' ;
69
710class BreadcrumbsBar extends StatefulWidget {
811 final PathParts path;
9- final ValueChanged <int >? onBreadcrumbPress;
12+ final ValueChanged <String >? onBreadcrumbPress;
1013 final ValueChanged <String >? onPathSubmitted;
1114 final List <Widget >? leading;
1215 final List <Widget >? actions;
@@ -86,38 +89,7 @@ class _BreadcrumbsBarState extends State<BreadcrumbsBar> {
8689 child: Container (
8790 height: double .infinity,
8891 alignment: AlignmentDirectional .centerStart,
89- child: focusNode.hasFocus
90- ? TextField (
91- decoration: const InputDecoration (
92- border: InputBorder .none,
93- contentPadding:
94- EdgeInsets .symmetric (horizontal: 8 ),
95- isCollapsed: true ,
96- ),
97- focusNode: focusNode,
98- controller: controller,
99- style: const TextStyle (fontSize: 14 ),
100- onSubmitted: widget.onPathSubmitted,
101- )
102- : ListView .separated (
103- scrollDirection: Axis .horizontal,
104- itemBuilder: (context, index) {
105- if (index == 0 ) {
106- return _buildItemChip (
107- index,
108- widget.path.root,
109- );
110- } else {
111- return _buildItemChip (
112- index,
113- widget.path.parts[index - 1 ],
114- );
115- }
116- },
117- itemCount: widget.path.parts.length + 1 ,
118- separatorBuilder: (context, index) =>
119- const VerticalDivider (width: 2 ),
120- ),
92+ child: _guts,
12193 ),
12294 ),
12395 ),
@@ -133,25 +105,111 @@ class _BreadcrumbsBarState extends State<BreadcrumbsBar> {
133105 );
134106 }
135107
136- Widget _buildItemChip (int index, String part) {
108+ Widget get _guts {
109+ if (focusNode.hasFocus) {
110+ return TextField (
111+ decoration: const InputDecoration (
112+ border: InputBorder .none,
113+ contentPadding: EdgeInsets .symmetric (horizontal: 8 ),
114+ isCollapsed: true ,
115+ ),
116+ focusNode: focusNode,
117+ controller: controller,
118+ style: const TextStyle (fontSize: 14 ),
119+ onSubmitted: widget.onPathSubmitted,
120+ );
121+ } else {
122+ final List <PathParts > actualParts;
123+
124+ // We need home folder on last position here to emulate a low priority entry
125+ final List <BuiltinFolder > sortedFolders = folderProvider.folders;
126+ final int homeIndex =
127+ sortedFolders.indexWhere ((e) => e.type == FolderType .home);
128+ sortedFolders.add (sortedFolders.removeAt (homeIndex));
129+
130+ final BuiltinFolder ? builtinFolder = sortedFolders.firstWhereOrNull (
131+ (e) => widget.path.toPath ().startsWith (e.directory.path),
132+ );
133+
134+ if (builtinFolder != null ) {
135+ final PathParts builtinParts =
136+ PathParts .parse (builtinFolder.directory.path);
137+ actualParts = [
138+ builtinParts,
139+ ...List .generate (
140+ widget.path.integralParts.length -
141+ builtinParts.integralParts.length,
142+ (index) =>
143+ widget.path.trim (index + builtinParts.integralParts.length),
144+ ),
145+ ];
146+ } else {
147+ actualParts = List .generate (
148+ widget.path.integralParts.length,
149+ (index) => widget.path.trim (index),
150+ );
151+ }
152+
153+ return ListView .separated (
154+ scrollDirection: Axis .horizontal,
155+ itemBuilder: (context, index) {
156+ final bool isInsideBuiltin = builtinFolder != null &&
157+ actualParts[index].toPath () == builtinFolder.directory.path;
158+
159+ return _BreadcrumbChip (
160+ path: actualParts[index],
161+ onTap: widget.onBreadcrumbPress,
162+ childOverride: isInsideBuiltin
163+ ? Row (
164+ children: [
165+ Icon (
166+ folderProvider.getIconForType (builtinFolder.type),
167+ size: 16 ,
168+ ),
169+ const SizedBox (width: 8 ),
170+ Text (Utils .getEntityName (builtinFolder.directory.path)),
171+ ],
172+ )
173+ : null ,
174+ );
175+ },
176+ itemCount: actualParts.length,
177+ separatorBuilder: (context, index) => const VerticalDivider (width: 2 ),
178+ );
179+ }
180+ }
181+ }
182+
183+ class _BreadcrumbChip extends StatelessWidget {
184+ const _BreadcrumbChip ({
185+ required this .path,
186+ this .onTap,
187+ this .childOverride,
188+ });
189+
190+ final PathParts path;
191+ final ValueChanged <String >? onTap;
192+ final Widget ? childOverride;
193+
194+ @override
195+ Widget build (BuildContext context) {
137196 return SizedBox (
138197 height: double .infinity,
139198 child: DragTarget <FileSystemEntity >(
140- onAccept: (data) =>
141- Utils .moveFileToDest (data, widget.path.toPath (index)),
199+ onAccept: (data) => Utils .moveFileToDest (data, path.toPath ()),
142200 builder: (context, candidateData, rejectedData) {
143201 return InkWell (
144202 child: Row (
145203 children: [
146204 Container (
147205 alignment: Alignment .center,
148206 padding: const EdgeInsetsDirectional .only (start: 12 , end: 4 ),
149- child: Text (part ),
207+ child: childOverride ?? Text (path.integralParts.last ),
150208 ),
151209 const Icon (Icons .chevron_right, size: 16 ),
152210 ],
153211 ),
154- onTap: () => widget.onBreadcrumbPress ? .call (index ),
212+ onTap: () => onTap ? .call (path. toPath () ),
155213 );
156214 },
157215 ),
0 commit comments