fourth commit
This commit is contained in:
@@ -154,6 +154,7 @@ class FileBrowserBloc extends Bloc<FileBrowserEvent, FileBrowserState> {
|
|||||||
currentPage: _currentPage,
|
currentPage: _currentPage,
|
||||||
pageSize: _pageSize,
|
pageSize: _pageSize,
|
||||||
totalPages: totalPages,
|
totalPages: totalPages,
|
||||||
|
sortBy: _sortBy,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -174,6 +175,12 @@ class FileBrowserBloc extends Bloc<FileBrowserEvent, FileBrowserState> {
|
|||||||
return b.lastModified.compareTo(a.lastModified);
|
return b.lastModified.compareTo(a.lastModified);
|
||||||
case 'size':
|
case 'size':
|
||||||
return b.size.compareTo(a.size);
|
return b.size.compareTo(a.size);
|
||||||
|
case 'type':
|
||||||
|
// Folders before files
|
||||||
|
if (a.type == FileType.folder && b.type == FileType.file) return -1;
|
||||||
|
if (a.type == FileType.file && b.type == FileType.folder) return 1;
|
||||||
|
// Within same type, sort by name
|
||||||
|
return a.name.compareTo(b.name);
|
||||||
default:
|
default:
|
||||||
return a.name.compareTo(b.name);
|
return a.name.compareTo(b.name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class NavigateToFolder extends FileBrowserEvent {
|
|||||||
class RefreshDirectory extends FileBrowserEvent {}
|
class RefreshDirectory extends FileBrowserEvent {}
|
||||||
|
|
||||||
class ApplySort extends FileBrowserEvent {
|
class ApplySort extends FileBrowserEvent {
|
||||||
final String sortBy; // name, date, size
|
final String sortBy; // name, date, size, type
|
||||||
|
|
||||||
const ApplySort(this.sortBy);
|
const ApplySort(this.sortBy);
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class DirectoryLoaded extends FileBrowserState {
|
|||||||
final int currentPage;
|
final int currentPage;
|
||||||
final int pageSize;
|
final int pageSize;
|
||||||
final int totalPages;
|
final int totalPages;
|
||||||
|
final String sortBy;
|
||||||
|
|
||||||
const DirectoryLoaded({
|
const DirectoryLoaded({
|
||||||
required this.files,
|
required this.files,
|
||||||
@@ -41,6 +42,7 @@ class DirectoryLoaded extends FileBrowserState {
|
|||||||
required this.currentPage,
|
required this.currentPage,
|
||||||
required this.pageSize,
|
required this.pageSize,
|
||||||
required this.totalPages,
|
required this.totalPages,
|
||||||
|
required this.sortBy,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -53,6 +55,7 @@ class DirectoryLoaded extends FileBrowserState {
|
|||||||
currentPage,
|
currentPage,
|
||||||
pageSize,
|
pageSize,
|
||||||
totalPages,
|
totalPages,
|
||||||
|
sortBy,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,12 @@ class _FileExplorerState extends State<FileExplorer> {
|
|||||||
},
|
},
|
||||||
child: const Text(
|
child: const Text(
|
||||||
'Create',
|
'Create',
|
||||||
style: TextStyle(color: AppTheme.accentColor),
|
style: TextStyle(
|
||||||
|
color: AppTheme.accentColor,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
decorationColor: AppTheme.accentColor,
|
||||||
|
decorationThickness: 1.5,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -180,31 +185,68 @@ class _FileExplorerState extends State<FileExplorer> {
|
|||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(top: 2),
|
padding: const EdgeInsets.only(top: 2),
|
||||||
child: IconButton(
|
child: Row(
|
||||||
icon: Icon(
|
children: [
|
||||||
_isSearching ? Icons.close : Icons.search,
|
IconButton(
|
||||||
color: AppTheme.accentColor,
|
icon: Icon(
|
||||||
),
|
_isSearching ? Icons.close : Icons.search,
|
||||||
onPressed: () {
|
color: AppTheme.accentColor,
|
||||||
if (_isSearching) {
|
),
|
||||||
setState(() {
|
onPressed: () {
|
||||||
_showField = false;
|
if (_isSearching) {
|
||||||
_isSearching = false;
|
setState(() {
|
||||||
_searchController.clear();
|
_showField = false;
|
||||||
_searchQuery = '';
|
_isSearching = false;
|
||||||
context.read<FileBrowserBloc>().add(ApplyFilter(''));
|
_searchController.clear();
|
||||||
});
|
_searchQuery = '';
|
||||||
} else {
|
context.read<FileBrowserBloc>().add(ApplyFilter(''));
|
||||||
setState(() {
|
});
|
||||||
_isSearching = true;
|
} else {
|
||||||
});
|
setState(() {
|
||||||
Future.delayed(const Duration(milliseconds: 150), () {
|
_isSearching = true;
|
||||||
setState(() {
|
});
|
||||||
_showField = true;
|
Future.delayed(const Duration(milliseconds: 150), () {
|
||||||
});
|
setState(() {
|
||||||
});
|
_showField = true;
|
||||||
}
|
});
|
||||||
},
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(width: 860),
|
||||||
|
|
||||||
|
BlocBuilder<FileBrowserBloc, FileBrowserState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
String currentSort = 'name';
|
||||||
|
if (state is DirectoryLoaded) {
|
||||||
|
currentSort = state.sortBy;
|
||||||
|
}
|
||||||
|
return DropdownButton<String>(
|
||||||
|
value: currentSort,
|
||||||
|
dropdownColor: AppTheme.accentColor.withAlpha(160),
|
||||||
|
|
||||||
|
style: const TextStyle(
|
||||||
|
color: AppTheme.primaryText,
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
underline: Container(), // Remove underline
|
||||||
|
items: const [
|
||||||
|
DropdownMenuItem(value: 'name', child: Text('Name')),
|
||||||
|
DropdownMenuItem(value: 'date', child: Text('Date')),
|
||||||
|
DropdownMenuItem(value: 'size', child: Text('Size')),
|
||||||
|
DropdownMenuItem(value: 'type', child: Text('Type')),
|
||||||
|
],
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
context.read<FileBrowserBloc>().add(
|
||||||
|
ApplySort(value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -259,7 +301,9 @@ class _FileExplorerState extends State<FileExplorer> {
|
|||||||
return BlocBuilder<FileBrowserBloc, FileBrowserState>(
|
return BlocBuilder<FileBrowserBloc, FileBrowserState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state is DirectoryLoading) {
|
if (state is DirectoryLoading) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return Center(
|
||||||
|
child: CircularProgressIndicator(color: AppTheme.accentColor),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (state is DirectoryError) {
|
if (state is DirectoryError) {
|
||||||
return Center(
|
return Center(
|
||||||
@@ -517,9 +561,9 @@ class _FileExplorerState extends State<FileExplorer> {
|
|||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
itemCount: state.filteredFiles.length,
|
itemCount: state.paginatedFiles.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final file = state.filteredFiles[index];
|
final file = state.paginatedFiles[index];
|
||||||
final isSelected = _selectedFilePath == file.path;
|
final isSelected = _selectedFilePath == file.path;
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@@ -611,6 +655,48 @@ class _FileExplorerState extends State<FileExplorer> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
// Pagination controls
|
||||||
|
if (state.totalPages > 1) ...[
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.chevron_left,
|
||||||
|
color: AppTheme.primaryText,
|
||||||
|
),
|
||||||
|
onPressed: state.currentPage > 1
|
||||||
|
? () {
|
||||||
|
context.read<FileBrowserBloc>().add(
|
||||||
|
LoadPage(state.currentPage - 1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'${state.currentPage} / ${state.totalPages}',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: AppTheme.primaryText,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.chevron_right,
|
||||||
|
color: AppTheme.primaryText,
|
||||||
|
),
|
||||||
|
onPressed: state.currentPage < state.totalPages
|
||||||
|
? () {
|
||||||
|
context.read<FileBrowserBloc>().add(
|
||||||
|
LoadPage(state.currentPage + 1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -29,6 +29,143 @@ class MockFileRepository implements FileRepository {
|
|||||||
size: 512,
|
size: 512,
|
||||||
lastModified: DateTime.now(),
|
lastModified: DateTime.now(),
|
||||||
),
|
),
|
||||||
|
// Add more files for pagination testing
|
||||||
|
FileItem(
|
||||||
|
name: 'presentation.pptx',
|
||||||
|
path: '/presentation.pptx',
|
||||||
|
type: FileType.file,
|
||||||
|
size: 2048,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'data.xlsx',
|
||||||
|
path: '/data.xlsx',
|
||||||
|
type: FileType.file,
|
||||||
|
size: 1024,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'video.mp4',
|
||||||
|
path: '/video.mp4',
|
||||||
|
type: FileType.file,
|
||||||
|
size: 102400,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'archive.zip',
|
||||||
|
path: '/archive.zip',
|
||||||
|
type: FileType.file,
|
||||||
|
size: 5120,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'notes.txt',
|
||||||
|
path: '/notes.txt',
|
||||||
|
type: FileType.file,
|
||||||
|
size: 256,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'config.json',
|
||||||
|
path: '/config.json',
|
||||||
|
type: FileType.file,
|
||||||
|
size: 128,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'script.js',
|
||||||
|
path: '/script.js',
|
||||||
|
type: FileType.file,
|
||||||
|
size: 256,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'styles.css',
|
||||||
|
path: '/styles.css',
|
||||||
|
type: FileType.file,
|
||||||
|
size: 512,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'index.html',
|
||||||
|
path: '/index.html',
|
||||||
|
type: FileType.file,
|
||||||
|
size: 1024,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'database.db',
|
||||||
|
path: '/database.db',
|
||||||
|
type: FileType.file,
|
||||||
|
size: 20480,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'logs',
|
||||||
|
path: '/logs',
|
||||||
|
type: FileType.folder,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'temp',
|
||||||
|
path: '/temp',
|
||||||
|
type: FileType.folder,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'backup',
|
||||||
|
path: '/backup',
|
||||||
|
type: FileType.folder,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'Music',
|
||||||
|
path: '/Music',
|
||||||
|
type: FileType.folder,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'Videos',
|
||||||
|
path: '/Videos',
|
||||||
|
type: FileType.folder,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'Projects',
|
||||||
|
path: '/Projects',
|
||||||
|
type: FileType.folder,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'Downloads',
|
||||||
|
path: '/Downloads',
|
||||||
|
type: FileType.folder,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'Pictures',
|
||||||
|
path: '/Pictures',
|
||||||
|
type: FileType.folder,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'Documents2',
|
||||||
|
path: '/Documents2',
|
||||||
|
type: FileType.folder,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'Archive',
|
||||||
|
path: '/Archive',
|
||||||
|
type: FileType.folder,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
|
FileItem(
|
||||||
|
name: 'OldFiles',
|
||||||
|
path: '/OldFiles',
|
||||||
|
type: FileType.folder,
|
||||||
|
lastModified: DateTime.now(),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -105,24 +105,13 @@ class _ModernGlassButtonState extends State<ModernGlassButton>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: widget.isLoading
|
child: DefaultTextStyle(
|
||||||
? SizedBox(
|
style: const TextStyle(
|
||||||
width: 20,
|
color: AppTheme.primaryText,
|
||||||
height: 20,
|
fontWeight: FontWeight.w600,
|
||||||
child: CircularProgressIndicator(
|
),
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(
|
child: widget.child,
|
||||||
AppTheme.primaryText,
|
),
|
||||||
),
|
|
||||||
strokeWidth: 2,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: DefaultTextStyle(
|
|
||||||
style: const TextStyle(
|
|
||||||
color: AppTheme.primaryText,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
child: widget.child,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user