How to access dynamic input fields values on button click in flutter - android

I am working on an attendance application where I assign wages to the workers. I want to store all the wages given to the workers into the database. But the problem is I want to access all the given values on button click. I have no idea how it can be done in flutter. I am a beginner.
I have given all the codes and the image of what output i want.
Image of Emulator
Here is my code...
ATTENDANCE SCREEN
...rest code...
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Upload Patti'),
content: SingleChildScrollView(
child: ListBody(
children: [
TextFormField(
controller: _mainWagesController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: "Enter Amount",
prefixIcon: Icon(Icons.wallet, color: Colors.blue),
),
),
],
),
),
actions: <Widget>[
ElevatedButton(
onPressed: () {
Navigator.pop(context);
newWages = _mainWagesController.text;
setState(() {});
},
child: const Text("Assign Wages"),
),
],
);
},
);
},
child: const Icon(Icons.check_circle),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.00),
child: Column(children: [
const SizedBox(
height: 20,
),
Center(
child: Text(
"Date : ${DateFormat.yMMMEd().format(DateTime.parse(widget.attendanceDate.toString()))}",
style: const TextStyle(fontSize: 20),
),
),
const SizedBox(
height: 20,
),
FutureBuilder(
future: SupervisorAttendanceServices.getAttendancesDetailsList(
widget.attendanceId),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
var data = snapshot.data['hamal'];
return ListView.builder(
itemCount: data.length,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return HamalAttendanceWidget(
workerId: data[index]['worker_id'],
name: data[index]['worker_name'],
wages: newWages,
masterAttendanceId: widget.attendanceId,
isPrensent: data[index]
['attendance_worker_presense']
.toString());
});
} else if (snapshot.hasError) {
return const Center(
child: Text("Something went wrong !"),
);
} else {
return const Center(child: LinearProgressIndicator());
}
},
),
]),
),
),
...rest code
widget
Widget build(BuildContext context) {
return Card(
child: Column(children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(
width: 10,
height: 50,
),
const Icon(FeatherIcons.user),
const SizedBox(
width: 20,
),
Text(
widget.name,
style: const TextStyle(fontSize: 18),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
width: 150,
height: 60,
child: TextFormField(
// onChanged: _onChangeHandler,
initialValue: widget.wages.toString(),
decoration: const InputDecoration(
hintText: "Wages",
prefixIcon: Icon(
Icons.wallet,
color: Colors.blue,
)),
)),
],
)
]),
);
}

I suggest you use a StateManager for your application, for example GetX
is a good solution. Create a controller file like the below:
// define this enum outside of class to handle the state of the page for load data
enum AppState { initial, loading, loaded, error, empty, disabled }
Rx<AppState> pageState = AppState.initial.obs;
class AttendanceCntroller extends GetxController{
RxList<dynamic> dataList=RxList<dynamic>();
#override
void onInit() {
//you can write other codes in here to handle data
pageState(AppState.loading);
dataList.value=
SupervisorAttendanceServices.getAttendancesDetailsList(attendanceId);
pageState(AppState.loaded);
super.onInit();
}
}
and in your view(UI) page, handle it in this way:
class AttendanceView extends GetView<AttendanceCntroller>{
#override
Widget body(BuildContext context) {
// TODO: implement body
return Obx( ()=> controller.pageState.value==AppState.loading ? const
Center(child: LinearProgressIndicator()) : ListView.builder(
itemCount: controller.dataList.length,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return HamalAttendanceWidget(
workerId: controller.dataList['worker_id'],
name: controller.dataList['worker_name'],
wages: newWages,
masterAttendanceId: widget.attendanceId,
isPrensent: controller.dataList[index]
['attendance_worker_presense']
.toString());
})
)
}
}
for more data read the GetX link and read clean architecture with the GetX sample repository of my GitHub it have advanced management of states with GetX with dependency injection handling.

If you want to have prefilled value in TextFormField, you can either use initialValue or controller parameter.
The value of controller parameter will help you to get/update the value of TextFormField.
For controller parameter refer below.
TextEditingController controller = TextEditingController(text: 'This is text will be pre-filled in TextFormField');
...
TextFormField(
controller: controller,
);
Create List or Map of those controllers.
List<TextEditingController> listOfControllers = [ controller1, controlle2,...];
Use for loop through this List on onClick() method of Button.
ElevatedButton(
onPressed: () {
for(var controllerItem in listOfControllers) {
print(controllerItem.text); // the value of TextFormField
}
},
)

Related

Switch Button not toggle flutter

hope all are doing well
i use a Switch Button in popup Form when pressing the floating action Button but its not working or the UI not updated when i pressed the Switch Button
I used it in a StatefulWidget but its not toggle until i press hot reload,if there any Suggestion please
here where i use the Switch :
void _openAlbumsDialog() {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Add an Album'),
content: Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _form,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
initialValue: _albumInitValues['name'],
decoration: InputDecoration(
helperText: 'Album Name',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
),
),
textInputAction: TextInputAction.next,
onSaved: ((newValue) {
_editedALbum = Album(
albumId: DateTime.now().toString(),
albumName: newValue!,
albumLinks: _editedALbum.albumLinks,
);
}),
validator: (value) {
if (value!.isEmpty) {
return 'Please insert a Value';
} else {
return null;
}
},
),
const SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Private'),
Consumer<Album>(
builder: (context, value, child) => Switch.adaptive(
mouseCursor: MouseCursor.uncontrolled,
value: _isPrivate,
onChanged: (value) {
final albumProvider =
Provider.of<Album>(context, listen: false);
albumProvider.togglePrivateAlbum();
setState(() {
_isPrivate = value;
});
},
),
),
actions: [
TextButton(
onPressed: () {
FocusScope.of(context).unfocus();
_isLoading
? const Center(
child: CircularProgressIndicator(),
)
: _saveAlbumFOrm();
},
child: const Text('Save'))
],
));
}
#override
Widget build(BuildContext context) {
return SpeedDial(
children: [
SpeedDialChild(
child: const Icon(Icons.folder),
backgroundColor: Colors.green,
foregroundColor: Colors.white,
label: 'Add a new Album',
labelStyle: const TextStyle(fontSize: 12.0),
onTap: _openAlbumsDialog,
),
],
);
}
}
void togglePrivate in Album Provider:
void togglePrivateAlbum() {
isPrivate = !isPrivate;
}
It happens so because you have to recreate your own StatefulBuilder(), so wrap your showDialog() with a StatefulBuilder() wich will then have the targeted state
StatefulBuilder(
builder:(context, StateSetter innerSetter){
....
//use the setter now
innerSetter(() {
_isPrivate = value;
});
....
})

How can I implement RefreshIndicator in Listview.Builder using Getx?

I want to implement RefreshIndicator in my Listview.builder but the problem is that when I place RefreshIndicator it's not working. And I search in Google the answer that I find is that place physic:
AlwaysScrollableScroll() in Listview.Builder and when I try it RefreshIndicator worked but Listview.Builder not working and I search in Google the answer that I find is that place physic:
NeverScrollableScroll() in Listview.Builder then My Listview.Builder is working OK but Refresh Indicator not working. What can I do?
I am little bit confused, what I can do: either I place AlwaysScrollableScroll() or NeverScrollable().
Here is my code:
return Scaffold(
body: SafeArea(
child: Padding(
padding: EdgeInsets.all(10),
child: Column(
children: [
FirstRow(
headingText: 'My Appointments',
context: context,
),
SizedBox(
height: 10,
),
Obx(() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AppButtonLarge(
selected: appointmentController.appointmentT ==
'upcomingAppointments' ??
false,
text: 'Upcoming',
onTap: onAppointmentChange),
AppButtonLarge(
selected: appointmentController.appointmentT ==
'pastAppointments' ??
false,
text: 'Past',
onTap: onAppointmentChange),
],
);
}),
SizedBox(
height: 10,
),
Expanded(
child: SingleChildScrollView(
physics: ScrollPhysics(),
child: Column(
children: [
GetX<DoctorAppointmentController>(
builder: (controller) {
// List<Appointment> controller.appointmentList=[];
// allAppointment.forEach((element) {
// if(element!=null){
// controller.appointmentList=controller.appointmentList+element;
// }
// });
// List<Appointment> controller.appointmentList=snapshot.data;
print(controller.appointmentList.length);
return controller.isLoading.value
? AppWidgetsCard.getProgressIndicator()
: controller.appointmentList.length > 0
? RefreshIndicator(
key: refreshKey,
onRefresh: () async{
await Navigator.pushReplacement(context, PageRouteBuilder(pageBuilder: (a,b,c)=>GetAllDrAppointments(),
transitionDuration: Duration(seconds: 3)));
},
child: Obx(()=>ListView.builder(
physics: const NeverScrollableScrollPhysics(),
// reverse: true,
itemCount: controller.appointmentList.length,
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (context, index) {
void goToNext() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
MyAppointments(
appointment: controller
.appointmentList[
index], isDoctor: true),
));
}
if (index ==
controller.appointmentList.length -
1) {
id = controller
.appointmentList[index].id;
return Column(
children: [
TextButton(
onPressed: goToNext,
child: AppWidgetsCard
.getAppointmentCard(
controller.appointmentList[index],
goToNext, isDoctor: true), ),
Obx(
() {
return controller.loadMore.value
? AppWidgetsCard
.getProgressIndicator()
: Container(
height: 40,
width: 200,
child: AppMethodButton(
selected: true,
text: 'Load More',
onTapMethod: () {
print('data');
controller
.fetchMoreAppointments(
id);
}),
);
},
)
],
);
} else {
return TextButton(
onPressed: goToNext,
child: AppWidgetsCard
.getAppointmentCard(
controller
.appointmentList[index],
goToNext, isDoctor: true));
}
},
),
),
)
: AppWidgetsCard.getEmptyCard('Appointment');
},
),
],
),
)),
],
),
)),
bottomNavigationBar: Container(
height: 50,
child: LastRow(
page: 'Appointment',
)),
);'''
About the ScrollPhysics there are different kinds of scrolling behavior. And AlwaysScrollableScroll means that the ListView is able to scroll no matter whether the body size of the ListView is larger than the assigned size. By default, the ListView only scrolls when the size of the body of the ListView is larger than the assigned parent size. So to adopt the RefreshIndicator does not matter with the scroll physics.
To avoid the cache issues of ListView to force the ListView update, you should update the data of the ListView. You would use the Obx wrapper with the ListView and create the list like below:
List<String> data = <String>[].obs;
Obx(()=> data.value.isEmpyty ? SizedBox() : ListView(children: data.value.map((value) => Text(value)).toList())

Problem with getting access to index of a map in flutter

I have a map<string, dynamic> ,and want to show it's components in cards by ListView.Builder, but the problem is by getting access to this map's indexes..
When running the app, it return "null" in the card!!
I have tried many solutions that I saw in StackOverFlow for a similar issues, but without a result.
Here where I identified my map:
var _item;
List listCount = [];
Map<String, dynamic> records = {};
String name;
And here where I give the var _item it's value:
MyCard(
colour: Colors.lightBlueAccent,
maker: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
StreamBuilder<int>(
stream: _stopWatchTimer2.rawTime,
initialData: 0,
builder: (context, snap) {
final value = snap.data;
final displayTime = StopWatchTimer.getDisplayTime(
value,
hours: _isHours2);
_item = displayTime;
return Padding(
padding: EdgeInsets.all(5.0),
child: Text(displayTime,
style: TextStyle(
fontSize: 30.0, color: Colors.white)),
);
},
),
],
),
),
And here in the "Save" button I give the 1st parameter of the map and assign in name variable:
createAlertDialog(buildContext, context) {
TextEditingController controller;
return showDialog(
context: context,
// barrierDismissible: false,
builder: (context) {
return AlertDialog(
title: Text(
'Type record name',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18.0),
),
content: TextField(
controller: controller,
onChanged: (value) {
name = value;
}),
actions: [
MaterialButton(
elevation: 5.0,
child: Text('Save'),
onPressed: () {
listCount.add(_item);
print(_item);
records[name] = _item;
print(records);
Navigator.pop(context);
},
),
MaterialButton(
elevation: 5.0,
child: Text('Cancel'),
onPressed: () {
Navigator.pop(context);
},
),
],
);
},
);
}
Finally here where I tried to show it in a card by listview.builder:
Container(
color: Colors.white,
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: records.length,
itemBuilder: (context, index) {
return MyCard(
colour: Colors.cyanAccent,
maker: Container(
width: 250.0,
height: 75.0,
child: Text(
'${records[index]}',
style: TextStyle(fontSize: 25.0),
textAlign: TextAlign.center,
),
),
);
},
),
),
I think the problem with the 2nd parameter of itemBuilder which it (index) because I replaced this keyword with another one randomly, and get the same result when running my app "Null".
This screenShot explain the problem:
You can get map entries as List as such:
final recordsEntries = records.entries.toList()
Then, you have a List of MapEntry and are able to access key and value associated for each item of the collection.
recordsEntries[index].key
recordsEntries[index].value

Flutter Firestore How to list people I only have chat history with?

This is a part of my chat homepage & it lists every user in my firestore database. But I want to make it list the only people I contacted with.
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.white,
title: Text(
'Sohbet',
style: TextStyle(color: primaryColor, fontWeight: FontWeight.bold),
),
iconTheme: IconThemeData(
color: Colors.black, //change your color here
),
centerTitle: true,
actions: <Widget>[
PopupMenuButton<Choice>(
onSelected: onItemMenuPress,
itemBuilder: (BuildContext context) {
return choices.map((Choice choice) {
return PopupMenuItem<Choice>(
value: choice,
child: Row(
children: <Widget>[
Icon(
choice.icon,
color: primaryColor,
),
Container(
width: 10.0,
),
Text(
choice.title,
style: TextStyle(color: primaryColor),
),
],
));
}).toList();
},
),
],
),
body: WillPopScope(
child: Stack(
children: <Widget>[
// List
Container(
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('users')
.limit(_limit)
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(themeColor),
),
);
} else {
return ListView.builder(
padding: EdgeInsets.all(10.0),
itemBuilder: (context, index) =>
buildItem(context, snapshot.data.documents[index]),
itemCount: snapshot.data.documents.length,
controller: listScrollController,
);
}
},
),
),
// Loading
Positioned(
child: isLoading ? const Loading() : Container(),
)
],
),
),
);
}
Widget buildItem(BuildContext context, DocumentSnapshot document) {
if (document.data()['id'] == _auth.currentUser.uid) {
return Container();
} else {
return Container(
child: FlatButton(
child: Row(
children: <Widget>[
Material(
child: document.data()['photoUrl'] != null
? CachedNetworkImage(
placeholder: (context, url) => Container(
child: CircularProgressIndicator(
strokeWidth: 1.0,
valueColor:
AlwaysStoppedAnimation<Color>(themeColor),
),
width: 50.0,
height: 50.0,
padding: EdgeInsets.all(15.0),
),
imageUrl: document.data()['photoUrl'],
width: 50.0,
height: 50.0,
fit: BoxFit.cover,
)
: Icon(
Icons.account_circle,
size: 50.0,
color: greyColor,
),
borderRadius: BorderRadius.all(Radius.circular(25.0)),
clipBehavior: Clip.hardEdge,
),
Flexible(
child: Container(
child: Column(
children: <Widget>[
Container(
child: Text(
'${document.data()['nickname']}',
style: TextStyle(color: primaryColor),
),
alignment: Alignment.centerLeft,
margin: EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 5.0),
),
/*Container(
child: Text(
'About me: ${document.data()['aboutMe'] ?? 'Not available'}',
style: TextStyle(color: primaryColor),
),
alignment: Alignment.centerLeft,
margin: EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 0.0),
)*/
],
),
margin: EdgeInsets.only(left: 20.0),
),
),
],
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Chat(
peerId: document.id,
peerAvatar: document.data()['photoUrl'],
)));
},
color: greyColor2,
padding: EdgeInsets.fromLTRB(25.0, 10.0, 25.0, 10.0),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
),
margin: EdgeInsets.only(bottom: 10.0, left: 5.0, right: 5.0),
);
}
}
Pics of my firestore data paths like:
Users collection
Messages collection
An example message doc
Tried:
set({friends array}) for per user in google sign in auth function but everytime I sign in it resets that array so there is no point updating that array because it resets that friends/contacts list everytime user signs in.
First you need to store the friends information, you can add a simple if check after google sign in auth function that if a document exists do not create a new document that would solve your resetting problem.
Assuming you have a currentUser object with firestore document id's
final friends = (await FirebaseFirestore.instance.collection("users").doc(currentUser.id).get()).data;
Get the friends list for the current user, you can convert it to list of user ids by mapping.
stream: FirebaseFirestore.instance
.collection('users')
.where("id",whereIn: friendsIds)
.limit(_limit)
.snapshots(),
i know this is super late but it might be useful for future search.
in every chat room(in your case it is messages), you need to create a field members which has a type of array or map. Inside that array, you need to store your id and the id of the person you are chatting with.
After that, you can just query the chat rooms .where((room) => room.members.contains(//your_id)).

Flutter : How to prevent rebuild of whole Reorderable Listview?

Currently I'm using flutter package 'Reorderables' to show a reorderable listview which contains several images.These images can be deleted from listview through a button , everything works fine. But the listview rebuild everytime when I delete an image. I'm using a class called 'ReorderableListviewManager' with ChangeNotifier to update images and Provider.of<ReorderableListviewManager>(context) to get latest images . The problem now is that using Provider.of<ReorderableListviewManager>(context) makes build() called everytime I delete an image , so the listview rebuid. I koow I
can use consumer to only rebuild part of widget tree, but it seems like that there's no place to put consumer in children of this Listview. Is there a way to rebuild only image but not whole ReorderableListview ? Thanks very much!
Below is my code:
class NotePicturesEditScreen extends StatefulWidget {
final List<Page> notePictures;
final NotePicturesEditBloc bloc;
NotePicturesEditScreen({#required this.notePictures, #required this.bloc});
static Widget create(BuildContext context, List<Page> notePictures) {
return Provider<NotePicturesEditBloc>(
create: (context) => NotePicturesEditBloc(),
child: Consumer<NotePicturesEditBloc>(
builder: (context, bloc, _) =>
ChangeNotifierProvider<ReorderableListviewManager>(
create: (context) => ReorderableListviewManager(),
child: NotePicturesEditScreen(
bloc: bloc,
notePictures: notePictures,
),
)),
dispose: (context, bloc) => bloc.dispose(),
);
}
#override
_NotePicturesEditScreenState createState() => _NotePicturesEditScreenState();
}
class _NotePicturesEditScreenState extends State<NotePicturesEditScreen> {
PreloadPageController _pageController;
ScrollController _reorderableScrollController;
List<Page> notePicturesCopy;
int longPressIndex;
List<double> smallImagesWidth;
double scrollOffset = 0;
_reorderableScrollListener() {
scrollOffset = _reorderableScrollController.offset;
}
#override
void initState() {
Provider.of<ReorderableListviewManager>(context, listen: false)
.notePictures = widget.notePictures;
notePicturesCopy = widget.notePictures;
_reorderableScrollController = ScrollController();
_pageController = PreloadPageController();
_reorderableScrollController.addListener(_reorderableScrollListener);
Provider.of<ReorderableListviewManager>(context, listen: false)
.getSmallImagesWidth(notePicturesCopy, context)
.then((imagesWidth) {
smallImagesWidth = imagesWidth;
});
super.initState();
}
#override
void dispose() {
_pageController.dispose();
_reorderableScrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
ReorderableListviewManager reorderableManager =
Provider.of<ReorderableListviewManager>(context, listen: false);
return SafeArea(
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
shape: Border(bottom: BorderSide(color: Colors.black12)),
iconTheme: IconThemeData(color: Colors.black87),
elevation: 0,
automaticallyImplyLeading: false,
titleSpacing: 0,
centerTitle: true,
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: IconButton(
padding: EdgeInsets.only(left: 20, right: 12),
onPressed: () => Navigator.of(context).pop(),
icon: Icon(Icons.close),
),
),
Text('編輯',
style: TextStyle(color: Colors.black87, fontSize: 18))
],
),
actions: <Widget>[
FlatButton(
onPressed: () {},
child: Text(
'下一步',
),
)
],
),
backgroundColor: Color(0xffeeeeee),
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Spacer(),
StreamBuilder<List<Page>>(
initialData: widget.notePictures,
stream: widget.bloc.notePicturesStream,
builder: (context, snapshot) {
notePicturesCopy = snapshot.data;
return Container(
margin: EdgeInsets.symmetric(horizontal: 20),
height: MediaQuery.of(context).size.height * 0.65,
child: PreloadPageView.builder(
preloadPagesCount: snapshot.data.length,
controller: _pageController,
itemCount: snapshot.data.length,
onPageChanged: (index) {
reorderableManager.updateCurrentIndex(index);
reorderableManager.scrollToCenter(
smallImagesWidth,
index,
scrollOffset,
_reorderableScrollController,
context);
},
itemBuilder: (context, index) {
return Container(
child: Image.memory(
File.fromUri(
snapshot.data[index].polygon.isNotEmpty
? snapshot.data[index]
.documentPreviewImageFileUri
: snapshot.data[index]
.originalPreviewImageFileUri)
.readAsBytesSync(),
gaplessPlayback: true,
alignment: Alignment.center,
),
);
}),
);
},
),
Spacer(),
Container(
height: MediaQuery.of(context).size.height * 0.1,
margin: EdgeInsets.symmetric(horizontal: 20),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: ReorderableRow(
scrollController: _reorderableScrollController,
buildDraggableFeedback: (context, constraints, __) =>
Container(
width: constraints.maxWidth,
height: constraints.maxHeight,
child: Image.memory(File.fromUri(
notePicturesCopy[longPressIndex]
.polygon
.isNotEmpty
? notePicturesCopy[longPressIndex]
.documentPreviewImageFileUri
: notePicturesCopy[longPressIndex]
.originalPreviewImageFileUri)
.readAsBytesSync()),
),
onReorder: (oldIndex, newIndex) async {
List<Page> result = await widget.bloc.reorderPictures(
oldIndex,
newIndex,
reorderableManager.notePictures);
_pageController.jumpToPage(newIndex);
reorderableManager.updateNotePictures(result);
reorderableManager
.getSmallImagesWidth(result, context)
.then((imagesWidth) {
smallImagesWidth = imagesWidth;
});
},
footer: Container(
width: 32,
height: 32,
margin: EdgeInsets.only(left: 16),
child: SizedBox(
child: FloatingActionButton(
backgroundColor: Colors.white,
elevation: 1,
disabledElevation: 0,
highlightElevation: 1,
child: Icon(Icons.add, color: Colors.blueAccent),
onPressed: notePicturesCopy.length >= 20
? () {
Scaffold.of(context)
.showSnackBar(SnackBar(
content: Text('筆記上限為20頁 !'),
));
}
: () async {
List<Page> notePictures =
await widget.bloc.addPicture(
reorderableManager.notePictures);
List<double> imagesWidth =
await reorderableManager
.getSmallImagesWidth(
notePictures, context);
smallImagesWidth = imagesWidth;
reorderableManager.updateCurrentIndex(
notePictures.length - 1);
reorderableManager
.updateNotePictures(notePictures);
_pageController
.jumpToPage(notePictures.length - 1);
},
),
),
),
children: Provider.of<ReorderableListviewManager>(
context)
.notePictures
.asMap()
.map((index, page) {
return MapEntry(
index,
Consumer<ReorderableListviewManager>(
key: ValueKey('value$index'),
builder: (context, manager, _) =>
GestureDetector(
onTapDown: (_) {
longPressIndex = index;
},
onTap: () {
reorderableManager.scrollToCenter(
smallImagesWidth,
index,
scrollOffset,
_reorderableScrollController,
context);
_pageController.jumpToPage(index);
},
child: Container(
margin: EdgeInsets.only(
left: index == 0 ? 0 : 12),
decoration: BoxDecoration(
border: Border.all(
width: 1.5,
color: index ==
manager
.getCurrentIndex
? Colors.blueAccent
: Colors.transparent)),
child: index + 1 <=
manager.notePictures.length
? Image.memory(
File.fromUri(manager
.notePictures[
index]
.polygon
.isNotEmpty
? manager
.notePictures[
index]
.documentPreviewImageFileUri
: manager
.notePictures[
index]
.originalPreviewImageFileUri)
.readAsBytesSync(),
gaplessPlayback: true,
)
: null),
),
));
})
.values
.toList()),
)),
Spacer(),
Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border(top: BorderSide(color: Colors.black12))),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
FlatButton(
onPressed: () async => await widget.bloc
.cropNotePicture(reorderableManager.notePictures,
_pageController.page.round())
.then((notePictures) {
reorderableManager.updateNotePictures(notePictures);
reorderableManager
.getSmallImagesWidth(notePictures, context)
.then((imagesWidth) {
smallImagesWidth = imagesWidth;
});
}),
child: Column(
children: <Widget>[
Icon(
Icons.crop,
color: Colors.blueAccent,
),
Container(
margin: EdgeInsets.only(top: 1),
child: Text(
'裁切',
style: TextStyle(color: Colors.blueAccent),
),
)
],
),
),
FlatButton(
onPressed: () {
int deleteIndex = _pageController.page.round();
widget.bloc
.deletePicture(
reorderableManager.notePictures, deleteIndex)
.then((notePictures) {
if (deleteIndex == notePictures.length) {
reorderableManager
.updateCurrentIndex(notePictures.length - 1);
}
reorderableManager.updateNotePictures(notePictures);
reorderableManager
.getSmallImagesWidth(notePictures, context)
.then((imagesWidth) {
smallImagesWidth = imagesWidth;
});
if (reorderableManager.notePictures.length == 0) {
Navigator.pop(context);
}
});
},
child: Column(
children: <Widget>[
Icon(
Icons.delete_outline,
color: Colors.blueAccent,
),
Container(
margin: EdgeInsets.only(top: 1),
child: Text(
'刪除',
style: TextStyle(color: Colors.blueAccent),
),
),
],
),
)
],
),
)
],
)),
);
}
}
You can't prevent a rebuild on your ReorderableListView widget because it will be rebuild every time there's an update on the Provider. What you can do here is to keep track the current index of all visible ListView items. When new data should be displayed coming from the Provider, you can retain the current indices of previous ListView items, and add the newly added items at the end of the list, or wherever you like.

Categories

Resources