11import 'package:async_redux/async_redux.dart' ;
22import 'package:flutter/material.dart' ;
33import 'package:stelaris/api/model/sound/sound_file_source.dart' ;
4+ import 'package:stelaris/api/paginated_result.dart' ;
45import 'package:stelaris/api/state/actions/sound/sound_actions.dart' ;
56import 'package:stelaris/api/state/actions/sound/sound_file_actions.dart' ;
67import 'package:stelaris/api/state/app_state.dart' ;
@@ -39,7 +40,7 @@ class _SoundFileEntriesState extends State<SoundFileEntryPage> {
3940 Widget build (BuildContext context) {
4041 return StoreConnector <AppState , SelectedSoundView >(
4142 vm: () => SelectedSoundState (),
42- onInit: (store) => InitSoundFileAction (),
43+ onInit: (store) => store. dispatch ( InitSoundFileAction () ),
4344 onDidChange: (context, store, viewModel) {
4445 _vm = viewModel;
4546 },
@@ -55,22 +56,10 @@ class _SoundFileEntriesState extends State<SoundFileEntryPage> {
5556 vm.hasNoFiles
5657 ? const Flexible (flex: 1 , child: EmptyDataWidget ())
5758 : Expanded (
58- child: ListView .builder (
59- controller: _scrollController,
60- padding: const EdgeInsets .all (8 ),
61- itemCount: vm.fileCount, // Example: 10 items
62- itemBuilder: (context, index) {
63- return ConstrainedBox (
64- constraints: const BoxConstraints (
65- minWidth: 220 ,
66- maxWidth: 400 ,
67- ),
68- child: SoundFileCard (eventModel: vm[index]),
69- );
70- },
71- ),
72- ),
73- ],
59+ child: _buildListView (vm),
60+ ),
61+ ]
62+ ,
7463 );
7564 },
7665 );
@@ -86,10 +75,11 @@ class _SoundFileEntriesState extends State<SoundFileEntryPage> {
8675 void _openAddDialog () {
8776 showDialog (
8877 context: context,
89- builder: (context) => SoundFileModal (
90- create: true ,
91- onSave:
92- ({
78+ builder: (context) =>
79+ SoundFileModal (
80+ create: true ,
81+ onSave:
82+ ({
9383 required name,
9484 required volume,
9585 required pitch,
@@ -111,7 +101,31 @@ class _SoundFileEntriesState extends State<SoundFileEntryPage> {
111101 context.dispatch (SoundFileSourceAddAction (fileSource));
112102 // Handle save logic here
113103 },
114- ),
104+ ),
105+ );
106+ }
107+
108+ Widget _buildListView (SelectedSoundView state) {
109+ final PaginatedResult <SoundFileSource > files = state.selected.files;
110+ final hasFooter = state.isLoadingFiles || state.hasNextPage;
111+ final itemCount = files.items.length + (hasFooter ? 1 : 0 );
112+ return ListView .builder (
113+ controller: _scrollController,
114+ padding: const EdgeInsets .all (8 ),
115+ itemCount: itemCount,
116+ clipBehavior: Clip .none,
117+ itemBuilder: (context, index) {
118+ if (index >= files.items.length) {
119+ return _buildFooter (state.isLoadingFiles);
120+ }
121+ return ConstrainedBox (
122+ constraints: const BoxConstraints (
123+ minWidth: 220 ,
124+ maxWidth: 400 ,
125+ ),
126+ child: SoundFileCard (eventModel: files.items[index]),
127+ );
128+ },
115129 );
116130 }
117131
@@ -121,10 +135,25 @@ class _SoundFileEntriesState extends State<SoundFileEntryPage> {
121135 final SelectedSoundView view = _vm! ;
122136
123137 if (_scrollController.position.pixels >=
124- _scrollController.position.maxScrollExtent - 200 &&
138+ _scrollController.position.maxScrollExtent - 200 &&
125139 view.hasNextPage &&
126140 ! view.isLoadingFiles) {
127141 view.onLoadMoreSoundFiles ();
128142 }
129143 }
130- }
144+
145+ Widget _buildFooter (bool isLoading) {
146+ return Padding (
147+ padding: const EdgeInsets .symmetric (vertical: 12 ),
148+ child: Center (
149+ child: isLoading
150+ ? const SizedBox (
151+ width: 20 ,
152+ height: 20 ,
153+ child: CircularProgressIndicator (strokeWidth: 2 ),
154+ )
155+ : const SizedBox .shrink (),
156+ ),
157+ );
158+ }
159+ }
0 commit comments