Looking up a deactivated widget's ancestor is unsafe.Flutter integaration test - flutter

type here
iam new to flutter and trying to learn test integration test package and i have come around this error.
The following assertion was thrown while finalizing the widget
tree:
Looking up a deactivated widget's ancestor is unsafe.
At this point the state of the widget's element tree is no longer
stable.
To safely refer to a widget's ancestor in its dispose() method,
save a reference to the ancestor by calling
dependOnInheritedWidgetOfExactType() in the widget's
didChangeDependencies() method.
and to the little understanding that i have this is the class throwing that error
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:modal_progress_hud/modal_progress_hud.dart';
import 'package:rflutter_alert/rflutter_alert.dart';
import 'package:wildai/helper/check_connectivity.dart';
import 'package:wildai/helper/common_methods.dart';
import 'package:wildai/helper/screen_config.dart';
import 'package:wildai/helper/toast_helper.dart';
import 'package:wildai/redux/actions.dart';
import 'package:wildai/redux/app_state.dart';
import 'package:wildai/redux/middleware/checkin_calls/checkin_update_get_data_call.dart';
import 'package:wildai/redux/middleware/homescreen_calls/update_single_view_calls.dart';
import 'package:wildai/src/models/dashboard/dashboard_model.dart';
import 'package:wildai/src/widgets/buttons/custom_radio_button.dart';
import 'package:wildai/src/widgets/custom_margins.dart';
import 'package:wildai/src/widgets/my_bottom_sheet.dart';
import 'package:wildai/utils/custom_alert.dart';
import 'package:wildai/utils/my_custom_text_theme.dart';
import 'package:wildai/helper/http_wrapper_custom.dart' as http;
import 'package:wildai/utils/my_redesign_icons.dart';
class EditCheckInOverlayPage extends StatefulWidget {
EditCheckInOverlayPage(this.title, this.avaiableCheckinItem,
this.updateCheckinsUrl, this.keyValue,
{Key key, this.showLoading = false, this.checkInRequestBody})
: super(key: key);
final String title;
final Map avaiableCheckinItem;
final String keyValue;
final String updateCheckinsUrl;
final Map checkInRequestBody;
bool showLoading;
\_EditCheckInOverlayPageState createState() =\> \_EditCheckInOverlayPageState();
}
class \_EditCheckInOverlayPageState extends State\<EditCheckInOverlayPage\> {
String micIconString = 'assets/icons/microphone.png';
Map responseBody = {'show_checkins': {}};
List\<AvaiableCheckinItemModel\> avaiableCheckinItems;
DateTime delayTime = DateTime.now();
final FlutterSecureStorage storage = new FlutterSecureStorage();
final store = StoreProvider.of\<AppState\>(SizeConfig.rootPagecOntext);
#override
void initState() {
super.initState();
avaiableCheckinItems = widget.avaiableCheckinItem\[widget.keyValue\];
List\<int\> requiredIntList = \[\];
for (var i = 0; i \< avaiableCheckinItems.length; i++) {
if (avaiableCheckinItems\[i\].require) {
requiredIntList.add(i);
}
}
for (int index in requiredIntList) {
var temp = avaiableCheckinItems\[index\];
avaiableCheckinItems.removeAt(index);
avaiableCheckinItems.insert(0, temp);
}
}
#override
void dispose() {
super.dispose();
if (mounted && !responseBody\['show_checkins'\].isEmpty) {
updateAvaiableCheckInsResponse();
print('///////////////////////////////');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.transparent,
body: Align(
alignment: Alignment.bottomCenter,
child: MyBottomModelSheet.titleSheet(
title: 'Edit ${widget.title} Check-ins',
height: 530,
onClose: () {
Navigator.pop(context);
},
body: sortDetailsContainer()),
),
);
}
Widget titleWidget() {
return Container(
decoration: BoxDecoration(color: Colors.white, boxShadow: \[
BoxShadow(
blurRadius: 25,
color: SizeConfig.primaryTextColour.withOpacity(.3),
)
\]),
child: Row(
children: \[
Expanded(
child: Padding(
padding: cmargin(top: 31, left: 30, right: 30),
child: Text(
'Edit ${widget.title} Check-Ins'.toUpperCase(),
style: MyCustomTextStyles().buttonTextSmall.copyWith(
color: SizeConfig.primaryTextColour.withOpacity(.4),
),
),
),
)
\],
),
);
}
Widget searchbarWidget() {
final \_formKey = GlobalKey\<FormState\>();
return Container(
color: Colors.white,
child: Padding(
padding: cmargin(left: 25, right: 25, bottom: 20, top: 22),
child: Container(
height: HelperMethods().getMyDynamicHeight(36),
decoration: BoxDecoration(
color: Color(0xff767680).withOpacity(.12),
borderRadius: BorderRadius.circular(10)),
child: Row(
children: \[
Padding(
padding: cmargin(left: 8, right: 7.37),
child: Container(
height: HelperMethods().getMyDynamicHeight(15.78),
width: HelperMethods().getMyDynamicWidth(15.63),
child: Icon(
MyAwesomeIcons.loupe,
color: Color(0xff8E8E93),
size: HelperMethods().getMyDynamicWidth(15.63),
),
),
),
Expanded(
child: Form(
key: \_formKey,
child: TextFormField(
onSaved: (String \_value) {
if (!\_formKey.currentState.validate() ||
\_value.isEmpty) {
return;
}
print(\_value);
},
style: MyCustomTextStyles().body.copyWith(
color: SizeConfig.primaryTextColour, height: 1),
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Search',
hintStyle: MyCustomTextStyles().body.copyWith(
color: Color(0xff3C3C43).withOpacity(.6),
height: 1.1,
),
),
),
),
),
Padding(
padding: cmargin(right: 9.63),
child: Container(
height: HelperMethods().getMyDynamicHeight(16.37),
width: HelperMethods().getMyDynamicWidth(11),
child: Image.asset(
micIconString,
color: Color(0xff8E8E93),
fit: BoxFit.contain,
),
),
),
\],
),
),
));
}
Widget closeButton() {
return Container(
color: Colors.transparent,
height: HelperMethods().getMyDynamicWidth(55),
width: SizeConfig.screenWidth,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: \[
Padding(
padding: cmargin(right: 30, bottom: 25),
child: InkWell(
borderRadius: BorderRadius.circular(50),
onTap: () {
Navigator.pop(context);
},
child: Container(
decoration: BoxDecoration(
color: Colors.black.withOpacity(.2),
shape: BoxShape.circle,
),
height: HelperMethods().getMyDynamicWidth(30),
width: HelperMethods().getMyDynamicWidth(30),
child: Icon(
MyAwesomeIcons.close,
key: Key("close_button"),
size: HelperMethods().getMyDynamicWidth(30),
)),
),
)
\],
),
);
}
Widget sortDetailsContainer() {
return Expanded(
child: ModalProgressHUD(
inAsyncCall: false,
color: Colors.transparent,
child:
Container(
width: SizeConfig.screenWidth,
color: Colors.white,
child: Padding(
padding: cmargin(),
child: ListView(
padding: cmargin(),
children: List.generate(
avaiableCheckinItems.length + 1,
(index) => index == avaiableCheckinItems.length
? Container(
height: 50,
)
: InkWell(
key: Key(avaiableCheckinItems[index].displayName),
onTap: () {
//delayTime = DateTime.now();
if (avaiableCheckinItems[index].require &&
avaiableCheckinItems[index].selected) {
return;
}
avaiableCheckinItems[index].selected =
!avaiableCheckinItems[index].selected;
addToResponseBody(avaiableCheckinItems[index]);
setState(() {});
},
child: Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: SizeConfig.primaryTextColour
.withOpacity(.4),
width: HelperMethods()
.getMyDynamicFontSize(.35)))),
child: Padding(
padding: cmargin(
left: 30, top: 20, bottom: 20, right: 30),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width:
HelperMethods().getMyDynamicWidth(244),
child: Row(
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
Flexible(
child: Text(
avaiableCheckinItems[index]
.displayName,
style: MyCustomTextStyles()
.formText
.copyWith(
color: SizeConfig
.primaryTextColour,
height: 1),
),
),
avaiableCheckinItems[index].require
? Text(
' (required)',
style: MyCustomTextStyles()
.labelSmall
.copyWith(
color: SizeConfig
.primaryTextColour),
)
: Container()
],
),
),
Opacity(
opacity: avaiableCheckinItems[index]
.require &&
avaiableCheckinItems[index].selected
? .2
: 1,
child: CustomRadioButton(
onTap: (value) {
if (avaiableCheckinItems[index]
.require &&
avaiableCheckinItems[index]
.selected) {
return;
}
avaiableCheckinItems[index].selected =
!avaiableCheckinItems[index]
.selected;
addToResponseBody(
avaiableCheckinItems[index]);
setState(() {});
},
selected:
avaiableCheckinItems[index].selected,
),
)
],
),
),
),
)),
),
),
),
),
);
}
addToResponseBody(AvaiableCheckinItemModel checkInItem) {
if (responseBody\['show_checkins'\].containsKey(checkInItem.modelName)) {
responseBody\['show_checkins'\]\[checkInItem.modelName\]
\[checkInItem.dbVariable\] = checkInItem.selected;
} else {
responseBody\['show_checkins'\]\[checkInItem.modelName\] = {
checkInItem.dbVariable: checkInItem.selected
};
}
print(responseBody);
return;
}
updateAvaiableCheckInsResponse() {
Conn().connectivityResult().then((conn) async {
if (conn) {
final store = StoreProvider.of\<AppState\>(SizeConfig.rootPagecOntext);
String body = json.encode(responseBody);
responseBody = {'show_checkins': {}};
String token = await storage.read(key: "wildUserToken");
DashboardCheckInsModel oldCheckins =
store.state.dashboardDataModel.checkins;
store.state.dashboardDataModel.checkins = DashboardCheckInsModel();
store.dispatch(SetDashboardObject(store.state.dashboardDataModel));
if (widget.showLoading) {
await \_createCheckins(oldCheckins);
}
final response =
await http.post(Uri.encodeFull(widget.updateCheckinsUrl),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
"Authorization": "Token $token"
},
body: body);
if (response.statusCode == 200) {
store.state.dashboardDataModel.checkins = oldCheckins;
store.dispatch(updateDashboardCheckinCall(forceRefresh: true));
} else if (response.statusCode == 400 || response.statusCode == 500) {
var res = json.decode(response.body);
store.dispatch(SetDashboardObject(store.state.dashboardDataModel));
String msg = res\['details'\];
CustomAlert().showCustomAlert(
context, "Oops! Something went wrong. ", msg, AlertType.info);
} else {
store.dispatch(SetDashboardObject(store.state.dashboardDataModel));
// throw Exception("failed to load calendar");
}
} else {
ShowMyToastHelper().showCheckConnectionToast(context, 2);
}
});
}
Future \_createCheckins(DashboardCheckInsModel dashboardCheckInsModel) async {
await Conn().connectivityResult().then((bool conn) async {
if (conn) {
String body = json.encode(widget.checkInRequestBody);
String token = await storage.read(key: "wildUserToken");
final response = await http.post(
Uri.encodeFull(dashboardCheckInsModel.endpoints.submitCheckin.url),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
"Authorization": "Token $token"
},
body: body);
if (response.statusCode == 200) {
store.dispatch(checkInsUpdateGetDataCall(silenty: true));
}
} else {
ShowMyToastHelper().showCheckConnectionToast(context, 2);
}
});
}
}
the problem is the script runs and performs its actions but the test fails.
any guidance would be appreciated.if there is anymore information needed for clarification i'll be happy to provide it.

Related

The list I created appears on all rows of the expansionpanellist

I'm new to Flutter and I'm having problems with expansion panel lists. Firstly, let me explain what I'm trying to do. I have a map application that gets the user's current location when they press the "start" button and displays the distance, date and time when the journey is finished and the "finish" button is pressed. Then, this information is stored in an expansion panel list on a separate "history" page.
However, I also have a third button called "add location". This button appears when the "start" button is pressed and allows the user to add their current location while the journey is ongoing. These locations are added to a list called "tripLocations" within my code. Then, when the "finish" button is pressed, all of this information is added to the expansion panel list.
My problem is that every time a journey is finished, the "tripLocations" list is displayed in the expansion panel list and shows all of the previous locations. However, I want each row in the expansion panel list to represent a single journey and only show the locations that were added during that journey. Essentially, there should be a "tripLocations" list for each row in the expansion panel list and it should be reset every time a new journey is started.
The following codes belong to my button.dart page.
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:material_dialogs/shared/types.dart';
import 'package:material_dialogs/widgets/buttons/icon_outline_button.dart';
import 'package:yolumukaydet/constant/constant.dart';
import 'package:latlong/latlong.dart';
import 'package:material_dialogs/material_dialogs.dart';
import 'package:lottie/lottie.dart';
import 'package:material_dialogs/widgets/buttons/icon_button.dart';
import 'package:geocoding/geocoding.dart';
import 'package:geocoding_platform_interface/geocoding_platform_interface.dart';
class GradientText extends StatelessWidget {
const GradientText(
this.text, {
required this.gradient,
this.style,
});
final String text;
final TextStyle? style;
final Gradient gradient;
#override
Widget build(BuildContext context) {
return ShaderMask(
blendMode: BlendMode.srcIn,
shaderCallback: (bounds) => gradient.createShader(
Rect.fromLTWH(0, 0, bounds.width, bounds.height),
),
child: Text(text, style: style),
);
}
}// texte gradient atamak için yapıldı
class StartHistory{
String city1;
String district1;
bool isExpanded;
StartHistory({
required this.city1,
required this.district1,
this.isExpanded = false,});
}//varış yeri konumu gösterme
class StartHistoryData {
static List<StartHistory> starthistory = [];
static void addStartHistory(String city1, String district1) {
starthistory.add(StartHistory(city1: city1, district1: district1));
}
}//varış yeri konumu gösterme
class TripLocation {
String address;
TripLocation({
required this.address,
});
}
class History {
String distance;
String date;
String time;
String city;
String district;
bool isExpanded;
History({
required this.distance,
required this.date,
required this.time,
required this.city,
required this.district,
this.isExpanded = false,
});
}
class HistoryData {
static List<History> history = [];
static List<TripLocation> tripLocations = [];
static void addHistory(String distance, String date, String time, String city, String district,) {
history.add(History(distance: distance, date: date, time: time, city: city, district: district,));
}
static void addTripLocation(TripLocation tripLocation) {
tripLocations.add(tripLocation);
}
}//mesafe ,tarih ve saat bilgileri
class Location {
final double latitude;
final double longitude;
final String note;
Location({
required this.latitude,
required this.longitude,
required this.note,
});
}//Konum Ekle butonu için konum alıyor
TextEditingController _noteController = TextEditingController();
List<Map<String, String>> _locations = [];
class ImageButton extends StatefulWidget {
#override
_ImageButtonState createState() => _ImageButtonState();
}
class _ImageButtonState extends State<ImageButton>with TickerProviderStateMixin {
bool _isStarted = false;
bool _isStopped = true;
void _getCurrentLocation() async {
final position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.best);
setState(() {
if (position != null) {
_currentPosition = position;
}
});
}
late Position _startPosition;
late Position _currentPosition;
double _distance = 0.0;
String _time = "";
Geolocator geolocator = Geolocator();
int _selectedIndex = 0;
final List<String> imageList = [
'assets/images/pwb3.png',
'assets/images/pwb4.png',
];
void _onPressed1() {
setState(() {
_selectedIndex = (_selectedIndex + 1) % imageList.length;
});
// buraya butona tıklama olayında yapmanız gereken diğer işlemleri ekleyebilirsiniz
}
late AnimationController _controller;
bool isOpened = false;
#override
void initState() {
_getCurrentLocation();
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 400),
);
super.initState();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
child: Align(
child: Stack(
children: <Widget>[
Container(
margin: EdgeInsets.only(left:160,top: 540.0,right:150),
width: 350.0,
height: 350.0,
child: FloatingActionButton(
child: Image.asset(
imageList[_selectedIndex],
height: 350.0,
width: 350.0,
),
onPressed: () {
setState(() {
_onPressed1();
isOpened = !isOpened;
if (isOpened) {
_controller.forward();
} else {
_controller.reverse();
}
});
},
),
),
Visibility(
visible: _isStarted,
child: Align(
alignment: Alignment.topRight,
child: Container(
width: 75.0,
height: 85.0,
margin: EdgeInsets.only(top: 540.0,right:50),
child: ScaleTransition(
scale: _controller,
child: FloatingActionButton(
backgroundColor: Constant.green,
child: Icon(Icons.add,size: 40.0,color: Constant.darkgrey,),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
String noteText = '';
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
child: Container(
padding: EdgeInsets.all(20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Konumunuz Eklenecek',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
),
),
SizedBox(height: 10.0),
Divider(
color: Colors.black,
height: 1.0,
),
SizedBox(height: 10.0),
Text(
'Eklediğiniz konum ve notlar geçmiş sayfasına kaydedilecektir.',
style: TextStyle(
fontSize: 8.0,
),
),
SizedBox(height: 10.0),
TextField(
controller: _noteController,
decoration: InputDecoration(hintText: 'Notunuzu Ekleyin'),
),
SizedBox(height: 10.0),
ElevatedButton(
onPressed: () async {
_getCurrentLocation();
//BURASI İL VE İLÇE KONUM ADLARINI ALIYOR VE CİTY İLE DİSTİRCT DEĞİŞKENİN ATIYOR
List<Placemark> placemarks = await placemarkFromCoordinates(_currentPosition.latitude, _currentPosition.longitude);
String _address = '${placemarks[0].administrativeArea}, ${placemarks[1].subAdministrativeArea}';
var tripLocation = TripLocation(address: _address);
HistoryData.addTripLocation(tripLocation);
Navigator.pop(context);
setState(() {});
print("tripLocation");
},
child: Text('Tamam'),
),
],
),
),
);
},
);
},
),
),
),
)),// KONUM EKLE BUTTONU
Visibility(
visible: _isStarted,
child: Align(
alignment: Alignment.topLeft,
child: Container(
width: 75.0,
height: 75.0,
margin: EdgeInsets.only(left:50,top: 540.0,),
child: ScaleTransition(
scale: _controller,
child: FloatingActionButton(
backgroundColor: Constant.green,
child: Icon(Icons.stop,size:40.0,color: Constant.darkgrey,),
onPressed: () async {
setState(() {
_isStopped = true;
_isStarted = false;
});
//BURASI İL VE İLÇE KONUM ADLARINI ALIYOR VE CİTY İLE DİSTİRCT DEĞİŞKENİN ATIYOR
List<Placemark> placemarks = await placemarkFromCoordinates(_currentPosition.latitude, _currentPosition.longitude);
String? city = placemarks.isNotEmpty ? placemarks[0].administrativeArea: '';
String? district = placemarks.isNotEmpty ? placemarks[1].subAdministrativeArea : '';
//
//
_currentPosition = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.best);
final Distance distance = new Distance();
var km = distance.as(
LengthUnit.Kilometer,
new LatLng(_startPosition.latitude, _startPosition.longitude),
new LatLng(_currentPosition.latitude,_currentPosition.longitude));
setState(() {
_time = DateTime.now().toString().substring(0, 19);
});
setState(() {
_distance = km as double;
});
showDialog(
context: context,
builder: (context) {
return AlertDialog(
backgroundColor: Constant.darkgrey,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50.0),
),
title: Container(
alignment: Alignment.center,
child: Text('Mesafe Bilgisi',
style: TextStyle(
color: Colors.white
),)),
content: Container(
height: 400,
width: 400,
decoration: BoxDecoration(
color: Constant.darkgrey,
borderRadius: BorderRadius.circular(50.0),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
GradientText(
'$_distance km',
style: const TextStyle(fontSize: 80,
fontFamily: 'digital',
),
gradient: LinearGradient(colors: [
Colors.white54,
Colors.white70,
]),
),
Text("Tarih ${DateTime.now().toString().split(' ').first} / SAAT ${DateTime.now().toString().split(' ').last.substring(0, 8)}",
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.white
),
),
Text(
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.white
),"$city / $district"),
Container(
width:350,
height: 45,
child: FloatingActionButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(5),
topRight: Radius.circular(5),
bottomLeft: Radius.circular(5),
bottomRight: Radius.circular(5),
),
),
backgroundColor: Constant.green,
child: Text("TAMAM",
style: TextStyle(
color: Constant.darkgrey,
),),
onPressed: () {
HistoryData.addHistory("$_distance km", "${DateTime.now().toString().split(' ').first}", "${DateTime.now().toString().split(' ').last.substring(0, 8)}","$city","$district",);
Navigator.of(context).pop();
},
),
),
],
),
),
);
},
);
},
),
),
),
),
),// STOP BUTTONU
Visibility(
visible: _isStopped,
child: Align(
alignment: Alignment.topCenter,
child: Container(
width: 75.0,
height: 75.0,
margin: EdgeInsets.only(top: 440.0),
child: ScaleTransition(
scale: _controller,
child: FloatingActionButton(
backgroundColor: Constant.green,
child: Icon(Icons.start,size:40.0,color: Constant.darkgrey,),
onPressed: () async {
List<Placemark> placemarks = await placemarkFromCoordinates(_currentPosition.latitude, _currentPosition.longitude);
String? city1 = placemarks.isNotEmpty ? placemarks[0].administrativeArea: '';
String? district1 = placemarks.isNotEmpty ? placemarks[1].subAdministrativeArea : '';
StartHistoryData.addStartHistory("$city1","$district1");
setState(() {
_isStarted = true;
_isStopped = false;
});
_startPosition = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.best);
},
),
),
),
),) // START BUTTONU
],
),
),
);
}
}
These codes belong to my historypage.dart file
import 'dart:ffi';
import 'package:flutter/material.dart';
import 'package:yolumukaydet/appbar.dart';
import 'package:yolumukaydet/constant/constant.dart';
import 'package:yolumukaydet/menu.dart';
import 'package:yolumukaydet/navigator_bar.dart';
import 'package:flutter/material.dart';
import 'package:yolumukaydet/button.dart';
class GecmisPage extends StatefulWidget {
#override
_GecmisPageState createState() => _GecmisPageState();
}
class _GecmisPageState extends State<GecmisPage> {
List<Map<String, dynamic>> _locations = [];
void _removeItem(String date) {
int indexToRemove = HistoryData.history.indexWhere((element) =>
element.date == date);
setState(() {
HistoryData.history.removeAt(indexToRemove);
});
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
duration: const Duration(milliseconds: 600),
content: Text('#$date tarihli bilginiz silindi.')));
} // delete tuşu
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Constant.darkgrey,
appBar: AppBarPage(),
body: Column(
children: [
ExpansionPanelList(
expansionCallback: (int index, bool isExpanded) {
setState(() {
HistoryData.history[index].isExpanded = !isExpanded;
});
},
children: List.generate(
HistoryData.history.length,
(index) {
return ExpansionPanel(
backgroundColor: Constant.opacgrey,
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: Text("Tarih: " + HistoryData.history[index].date + " - " + HistoryData.history[index].city + "/" + HistoryData.history[index].district),
leading: IconButton(
onPressed: () => _removeItem(HistoryData.history[index].date),
icon: const Icon(
Icons.delete,
color: Constant.green,
),
),
);
},
body: Container(
height: 500,
width: double.infinity,
color: Constant.opacgrey,
child: Container(
height: 250,
width: double.infinity,
decoration: BoxDecoration(),//boş bırakıldı
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Mesafe :", style: TextStyle(fontSize: 18.0)),
Text("${HistoryData.history[index].distance}",
style: TextStyle(fontSize: 18.0)),
Text(" / ", style: TextStyle(fontSize: 18.0)),
Text("Saat :", style: TextStyle(fontSize: 18.0)),
Text("${HistoryData.history[index].time}",
style: TextStyle(fontSize: 18.0)),
],
),
),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Varış Yeri :", style: TextStyle(fontSize: 18.0)),
Text("${StartHistoryData.starthistory[index].city1}",
style: TextStyle(fontSize: 18.0)),
Text(" / ", style: TextStyle(fontSize: 18.0)),
Text("${StartHistoryData.starthistory[index].district1}",
style: TextStyle(fontSize: 18.0)),
],
),
),
Container(
height: 400,
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(color: Colors.black,width: 2)
),
child:
ListView.builder(
itemCount: HistoryData.tripLocations.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(HistoryData.tripLocations[index].address),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
setState(() {
HistoryData.tripLocations.removeAt(index);
});
},
),
);
},
)
),
],
),
),
),
),
isExpanded: HistoryData.history[index].isExpanded,
);
},
),
),
],
),
);
}
}
Where should I make the corrections here? I'm waiting for your help, thank you.

Cannot remove strings from list in flutter

I have a page to select images from the gallery and add those urls in a list that will be passed to another page after popping. Everything works fine the first time the user accesses the page, however, when coming back to the Image View with an already populated list (passed as argument), whenever I try to edit/removing elements from it, the list remains the same. But the images I want to delete get removed correctly from the firebase storage.
I will attach the code I am trying to use. I am new to flutter so I will really appreciate your help.
If you have any questions about the code feel free to ask!
import 'dart:convert';
import 'dart:io';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:moneybo/utilities/dialogs/delete_dialog.dart';
import 'package:moneybo/utilities/dialogs/error_dialog.dart';
import 'package:moneybo/utilities/generics/get_arguments.dart';
class ImageView extends StatefulWidget {
const ImageView({super.key});
#override
State<ImageView> createState() => _ImageViewState();
}
class _ImageViewState extends State<ImageView> {
late final ImagePicker _picker;
late final ImageCache _cache;
List<String> imageUrls = [];
List<bool> isImageSelected = [];
List<String> imagesToDelete = [];
String imgs = "";
bool _isSelected = false;
#override
void initState() {
_picker = ImagePicker();
_cache = ImageCache();
super.initState();
}
#override
Widget build(BuildContext context) {
List<String>? newImageList;
final imageList = context.getArgument<String>();
if (imageList != null && imageList.isNotEmpty) {
newImageList = (jsonDecode(imageList) as List<dynamic>).cast<String>();
for (String url in newImageList) {
if (!imageUrls.contains(url)) {
imageUrls.add(url);
}
}
imgs = jsonEncode(imageUrls);
for (String element in imageUrls) {
isImageSelected.add(false);
}
}
return GestureDetector(
onTap: () {
List<bool> newIsImageSelected = [];
for (String element in imageUrls) {
newIsImageSelected.add(false);
}
setState(() {
_isSelected = false;
isImageSelected = newIsImageSelected;
imagesToDelete = [];
});
print(imageUrls);
// print(imgs);
},
child: Scaffold(
appBar: AppBar(
centerTitle: true,
toolbarHeight: 73,
leading: Padding(
padding: const EdgeInsets.only(top: 25, left: 5),
child: TextButton(
child: const Text(
"OK",
style: TextStyle(fontSize: 18, color: Colors.white),
),
onPressed: () => Navigator.pop(context, imgs),
),
),
actions: [
Padding(
padding: const EdgeInsets.only(right: 20, top: 20),
child: isImageSelected.contains(true)
? IconButton(
onPressed: () async {
final shouldDelete = await showDeleteDialog(
context, "Delete this image(s)?");
if (shouldDelete) {
for (String deleteImage in imagesToDelete) {
imageUrls.removeWhere((image) =>
image.hashCode == deleteImage.hashCode);
await FirebaseStorage.instance
.refFromURL(deleteImage)
.delete();
}
imgs = jsonEncode(imageUrls);
List<bool> newIsImageSelected = [];
for (String element in imageUrls) {
newIsImageSelected.add(false);
}
_cache.clear();
_cache.clearLiveImages();
setState(() {
isImageSelected = newIsImageSelected;
imagesToDelete = [];
});
}
},
icon: SizedBox(
height: 25,
child: Image.asset(
"lib/icons/bin.png",
color: Colors.white,
)))
: IconButton(
icon: const Icon(Icons.add_a_photo_rounded),
onPressed: () async {
List<XFile>? images = await _picker.pickMultiImage();
String uniqueFileName =
DateTime.now().microsecondsSinceEpoch.toString();
Reference referenceRoot =
FirebaseStorage.instance.ref();
Reference referenceDirImages =
referenceRoot.child("images");
try {
for (XFile image in images) {
uniqueFileName =
(int.parse(uniqueFileName) + 1).toString();
Reference referenceImageToUpload =
referenceDirImages.child(uniqueFileName);
await referenceImageToUpload
.putFile(File(image.path));
String imageUrl =
await referenceImageToUpload.getDownloadURL();
imageUrls.add(imageUrl);
isImageSelected.add(false);
}
} catch (error) {
if (mounted) {
showErrorDialog(context, "$error");
}
}
imgs = jsonEncode(imageUrls);
setState(() {});
},
),
),
],
title: const Padding(
padding: EdgeInsets.only(top: 30.0),
child: Text(
"Add images",
style: TextStyle(fontSize: 16),
),
),
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[
Color.fromRGBO(24, 92, 92, 1),
Color.fromRGBO(33, 108, 108, 1),
Color.fromRGBO(40, 121, 121, 1),
Color.fromRGBO(48, 136, 136, 1),
Color.fromRGBO(50, 139, 139, 1),
Color.fromRGBO(54, 143, 143, 1),
Color.fromRGBO(57, 145, 145, 1),
]),
),
),
),
body: imageUrls.isNotEmpty
? Padding(
padding: const EdgeInsets.only(top: 20, left: 8, right: 8),
child: GridView.builder(
itemCount: imageUrls.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: imageUrls.length > 1 ? 2 : 1,
mainAxisSpacing: imageUrls.isNotEmpty ? 8 : 0,
crossAxisSpacing: imageUrls.isNotEmpty ? 8 : 0,
),
itemBuilder: (context, index) {
return GestureDetector(
onLongPress: () {
setState(() {
_isSelected = true;
});
},
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: _isSelected
? Colors.green
: const Color.fromRGBO(11, 68, 68, 1),
width: _isSelected ? 4 : 2),
borderRadius:
const BorderRadius.all(Radius.circular(20))),
child: Stack(
children: [
Center(
child: Image.network(
imageUrls[index],
fit: BoxFit.contain,
),
),
_isSelected
? Align(
alignment: Alignment.topRight,
child: Transform.scale(
scale: 1.3,
child: Checkbox(
side: const BorderSide(
width: 2,
color: Color.fromRGBO(
11, 68, 68, 1)),
shape: const CircleBorder(),
value: isImageSelected[index],
onChanged: (bool? value) {
setState(() {
isImageSelected[index] = value!;
});
if (isImageSelected[index]) {
if (imagesToDelete.isNotEmpty) {
imagesToDelete.insert(
index, imageUrls[index]);
} else {
imagesToDelete
.add(imageUrls[index]);
}
} else if (!isImageSelected[
index] &&
imagesToDelete.contains(
imageUrls[index])) {
imagesToDelete
.remove(imageUrls[index]);
} else {
return;
}
print(imagesToDelete);
}),
),
)
: const SizedBox(),
],
),
),
);
},
),
)
: Padding(
padding: const EdgeInsets.only(bottom: 150),
child: Center(
child: SizedBox(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(
Icons.image_not_supported_outlined,
size: 150,
color: Color.fromRGBO(168, 168, 168, 1),
),
SizedBox(height: 25),
Text(
"No images yet",
style: TextStyle(
color: Color.fromRGBO(168, 168, 168, 1),
fontSize: 25,
),
)
]),
)),
),
),
);
}
}

Video player has error ExoPlaybackException: Source error

I am currently using better_player: 0.0.82 to play video files(.m3u8 format) which I am getting from the server using signed cookies.
I get the below error sometimes, after restarting the current video or after a certain time of video playing. And after getting this error all my other app assets which needs cookies to authorize also show error code: 403(forbidden). I am not using any cookie manager but storing the cookies in a map and then accessing its value wherever required.
Flutter version - 3.0.5(stable)
I was using chewie earlier but got the same error.
Been stuck at it for the last 20 days. Any help would be highly appreciated.
Console & Log Error:-
2022-08-31 17:11:39.644 2155-2639/ E/ExoPlayerImplInternal: Playback error
com.google.android.exoplayer2.ExoPlaybackException: Source error
at com.google.android.exoplayer2.ExoPlayerImplInternal.handleIoException(ExoPlayerImplInternal.java:641)
at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:613)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.os.HandlerThread.run(HandlerThread.java:67)
Caused by: com.google.android.exoplayer2.upstream.HttpDataSource$InvalidResponseCodeException: Response code: 403
at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:396)
at com.google.android.exoplayer2.upstream.StatsDataSource.open(StatsDataSource.java:84)
at com.google.android.exoplayer2.upstream.DataSourceInputStream.checkOpened(DataSourceInputStream.java:99)
at com.google.android.exoplayer2.upstream.DataSourceInputStream.open(DataSourceInputStream.java:62)
at com.google.android.exoplayer2.upstream.ParsingLoadable.load(ParsingLoadable.java:174)
at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:412)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
at java.lang.Thread.run(Thread.java:1012)
2022-08-31 17:11:39.660 385-385/? W/RanchuHwc: presentDisplay display has no layers to compose, flushing client target buffer.
2022-08-31 17:11:39.662 2155-2218/ I/flutter: ----------------FIREBASE CRASHLYTICS----------------
2022-08-31 17:11:39.662 2155-2218/ I/flutter: PlatformException(VideoError, Video player had error com.google.android.exoplayer2.ExoPlaybackException: Source error, , null)
2022-08-31 17:11:39.664 2155-2218/ I/flutter:
2022-08-31 17:11:39.664 2155-2218/ I/flutter: ----------------------------------------------------
VideoPlayerScreen.dart file code :-
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:wakelock/wakelock.dart';
import 'package:better_player/better_player.dart';
import 'package:http/http.dart' as http;
import '../../api/api.dart';
import '../../common/Utils/Colors.dart';
import '../../common/Utils/TextStyle.dart';
import '../../common/Widgets/NotificationDialog.dart';
import '../../common/Widgets/ShowExceptionAlertDialog.dart';
import '../../models/m3u8pass.dart';
import '../../navigation/Navigation.dart';
import '../../provider/AuthProvider.dart';
import '../../provider/MyBooksProvider.dart';
class VideoPlayerScreen extends StatefulWidget {
final int? tocContentId;
VideoPlayerScreen({this.tocContentId});
#override
State<VideoPlayerScreen> createState() => _VideoPlayerScreenState();
}
late List<M3U8pass> m3u8List;
List<String>? qualities;
String? selectedQuality;
class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
TargetPlatform? platform;
String? m3u8Content;
late BetterPlayerController _betterPlayerController;
List<Map<String, dynamic>>? cookieResults;
BetterPlayerDataSource? dataSource;
#override
void initState() {
super.initState();
cookieResults = Provider.of<AuthBase>(context, listen: false).cookie;
Wakelock.enable();
m3u8List = [];
qualities = [];
selectedQuality = 'Auto';
getVideoUrl();
BetterPlayerConfiguration betterPlayerConfiguration =
BetterPlayerConfiguration(
aspectRatio: 16 / 9,
fit: BoxFit.contain,
autoPlay: true,
fullScreenByDefault: false,
looping: false,
);
_betterPlayerController = BetterPlayerController(betterPlayerConfiguration);
}
Future<void> getVideoUrl() async {
try {
final response =
await Provider.of<MyBooksProvider>(context, listen: false)
.getTocContent(context, widget.tocContentId);
print('TocContentId from VideoPlayerScreen => ${widget.tocContentId} ');
print('response => ${response!.data['data']}');
BetterPlayerDataSource dataSource = BetterPlayerDataSource(
BetterPlayerDataSourceType.network,
'${API.bookContentUrl}${response.data['data']}',
headers: {
'cookie': (cookieResults != null && cookieResults!.isNotEmpty) ?
"${cookieResults!.first.keys.elementAt(0)}=${cookieResults!.first.values.elementAt(0)};${cookieResults![1].keys.elementAt(0)}=${cookieResults![1].values.elementAt(0)};${cookieResults!.last.keys.elementAt(0)}=${cookieResults!.last.values.elementAt(0)}" : ""
},
);
_betterPlayerController.setupDataSource(dataSource);
} catch (e) {
showExceptionAlertDialog(
context,
title: 'Unexpected Error',
exception: Exception('Please try again later'),
);
}
}
#override
void dispose() {
_betterPlayerController.pause();
_betterPlayerController.dispose();
Wakelock.disable();
super.dispose();
}
/// responsible for checking video urls and storing them in a list
Future<void> m3u8video(String videoUrl) async {
m3u8List.clear();
m3u8List.add(M3U8pass(dataquality: "Auto", dataurl: videoUrl));
RegExp regExp = RegExp(
r"#EXT-X-STREAM-INF:(?:.*,RESOLUTION=(\d+x\d+))?,?(.*)\r?\n(.*)",
caseSensitive: false,
multiLine: true,
);
if (mounted) {
setState(() {
m3u8Content = null;
});
}
if (m3u8Content == null) {
http.Response response = await http.get(Uri.parse(videoUrl));
if (response.statusCode == 200) {
m3u8Content = utf8.decode(response.bodyBytes);
}
}
List<RegExpMatch> matches = regExp.allMatches(m3u8Content!).toList();
matches.forEach(
(RegExpMatch regExpMatch) {
String quality = (regExpMatch.group(1)).toString();
String sourceurl = (regExpMatch.group(3)).toString();
final netRegx = RegExp(r'^(http|https):\/\/([\w.]+\/?)\S*');
final netRegx2 = RegExp(r'(.*)\r?\/');
final isNetwork = netRegx.hasMatch(sourceurl);
final match = netRegx2.firstMatch(videoUrl);
String url;
if (isNetwork) {
url = sourceurl;
} else {
final dataurl = match!.group(0);
url = "$dataurl$sourceurl";
print("--- hls chlid url intergration ---\nchild url :$url");
}
m3u8List.add(M3U8pass(dataquality: quality, dataurl: url));
},
);
}
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return Container(
color: primaryColor,
child: SafeArea(
child: Scaffold(
body: Stack(
fit: StackFit.expand,
children: [
Column(
children: <Widget>[
Expanded(
child: Center(
child: BetterPlayer(controller: _betterPlayerController),
// child: _chewieController != null &&
// _chewieController!
// .videoPlayerController.value.isInitialized
// ? Chewie(
// controller: _chewieController!,
// )
// : Column(
// mainAxisAlignment: MainAxisAlignment.center,
// children: const [
// CircularProgressIndicator(),
// SizedBox(height: 20),
// Text('Loading'),
// ],
// ),
),
),
],
),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
height: size.height * .17,
decoration: BoxDecoration(
color: primaryColor,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(size.width * .13),
bottomRight: Radius.circular(size.width * .13),
),
),
child: Container(
alignment: Alignment.topLeft,
margin: EdgeInsets.symmetric(
horizontal: size.width * .05,
vertical: size.width * .05,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
child: Image.asset(
"assets/images/cengage_white.png",
height: 25,
),
),
Row(
children: <Widget>[
GestureDetector(
onTap: () =>
Navigation.moveToSearchScreen(context),
child: Icon(
Icons.search_outlined,
color: secondaryColor,
),
),
const SizedBox(width: 10),
InkWell(
onTap: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: secondaryColor,
insetPadding: EdgeInsets.zero,
content: NotificationDialog(),
),
);
},
child: Icon(
Icons.notifications_outlined,
color: secondaryColor,
),
),
const SizedBox(width: 10),
// Consumer<CartQuantityProvider>(
// builder: (_, value, __) {
// return GestureDetector(
// onTap: () =>
// Navigation.openCartBottomModal(
// context,
// ),
// child: Badge(
// badgeColor: Colors.amber,
// badgeContent:
// Text('${value.cartQuantity}'),
// animationType:
// BadgeAnimationType.scale,
// child: IconButton(
// constraints: const BoxConstraints(),
// padding: const EdgeInsets.symmetric(
// horizontal: 6,
// ),
// splashRadius: 20.0,
// icon: Icon(
// Icons.shopping_cart_outlined,
// color: secondaryColor,
// ),
// onPressed: () =>
// Navigation.openCartBottomModal(
// context,
// ),
// ),
// position: BadgePosition.topEnd(
// end: 0,
// top: -MediaQuery.of(context)
// .size
// .width *
// 0.025,
// ),
// ),
// );
// },
// ),
const SizedBox(width: 10),
],
),
],
),
],
),
),
),
],
),
Consumer<MyBooksProvider>(
builder: (_, value, __) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
margin: EdgeInsets.only(
top: size.width * 0.20,
left: size.width * 0.05,
),
child: Icon(
Icons.arrow_back_ios,
size: size.width * 0.05,
color: Colors.white,
),
),
),
value.lastReadBookImage != null
? Container(
margin: EdgeInsets.only(
top: size.width * 0.21,
left: size.width * 0.02,
),
height: size.width * 0.25,
width: size.width * 0.18,
child: Image.file(value.lastReadBookImage!),
)
: Container(
margin: EdgeInsets.only(
top: size.width * 0.22,
left: size.width * 0.03,
),
height: 80,
width: 60,
color: Colors.blue,
),
Expanded(
child: Container(
margin: EdgeInsets.only(
top: size.width * 0.22,
left: size.width * 0.05,
right: size.width * 0.1,
),
child: Text(
'${value.bookTitle}',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: CustomTextStyle.bookTitleS(
size.width,
),
),
),
),
],
);
},
),
],
),
),
),
);
}
}

Keyboard trigger the api call while closing and opening in flutter?

Keyboard triggers the api calling while closing and opening
I have search bar I didn't pass anything inside the search bar but when I click the search bar The api call will run.
I don't know why that thing was happen I tried several ways but It won't work
Even Inside the keyboard I choose the one hand keyboard that time also api will run
Also tried the Focusnode also.
class PopupNaviagator extends StatefulWidget {
var body;
String leadsName = "";
Map<String, dynamic> listValues;
PopupNaviagator(this.listValues);
var data;
String pageData = "";
double totalPageCount = 0;
int pageIncrementer = 1;
#override
_PopupNavigatorState createState() => _PopupNavigatorState();
}
class _PopupNavigatorState extends State<PopupNaviagator> {
Utils util = new Utils();
bool isSearching = false;
#override
void initState() {
super.initState();
}
String searchingText = "";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
titleSpacing: 0,
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: util.getColors(1001),
stops: [0.1, 5.0],
),
),
),
title: !isSearching
? Text("Select Search Type")
: TextField(
autofocus: true,
autocorrect: true,
onChanged: (value) {
searchingText = value;
},
cursorColor: Colors.white,
onSubmitted: (value) {
searchingText = value;
campaignAPI(
widget.listValues["colname"].toString(),
widget.pageIncrementer.toString(),
searchingText, // search key
1, // flag
);
},
style: TextStyle(color: Colors.white),
decoration: InputDecoration(
icon: Icon(Icons.search),
hintText: "Search ",
hintStyle: TextStyle(color: Colors.white)),
),
actions: <Widget>[
isSearching
? IconButton(
icon: Icon(Icons.cancel),
onPressed: () {
setState(() {
searchingText = "";
this.isSearching = false;
campaignAPI(
widget.listValues["colname"].toString(),
widget.pageIncrementer.toString(),
searchingText, // search key
1, // flag
);
/* filteredCountries = countries;*/
});
},
)
: IconButton(
icon: Icon(Icons.search),
onPressed: () {
setState(() {
this.isSearching = true;
});
},
)
],
),
body: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(5))),
child: Scaffold(
body: FutureBuilder(
future: campaignAPI(widget.listValues["colname"].toString(), "1",
searchingText, 0),
builder: (context, snapshot) {
if (snapshot.hasData) {
var body;
if (widget.data == null) {
body = snapshot.data;
} else {
body = widget.data;
}
final records = body["list_records"];
widget.pageData = body["PageValues"];
widget.totalPageCount =
(body["TotalRecordCount"] / 5).toDouble();
return Scaffold(
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
if (records.length != 0) ...[
Expanded(
child: ListView.builder(
padding: EdgeInsets.only(
top: 0,
bottom: 0,
),
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: records.length,
itemBuilder: (context, index) {
Map<String, dynamic> pickerValues =
records[index];
String titleName =
pickerValues["viewcol_value"]
.toString();
return Container(
margin: const EdgeInsets.all(5.00),
decoration: new BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.grey, width: 1.0),
borderRadius: new BorderRadius.all(
Radius.circular(3.0)),
),
child: FlatButton(
padding: EdgeInsets.all(8.0),
onPressed: () {
setState(() {
List<String> strArr = [
titleName,
pickerValues["rec_id"].toString(),
widget.listValues["colname"]
];
Navigator.pop(context, strArr);
});
},
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(titleName.toString()),
Icon(
Icons.check_circle_outlined,
color: Colors.grey,
),
]),
),
);
}),
),
] else ...[
Container(
height: MediaQuery.of(context).size.height / 1.4,
child: Center(
child: Container(
padding: EdgeInsets.all(65),
child: Text(
"No Record Found",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.black),
),
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey,
width: 2,
),
)),
),
),
],
if (records.length != 0) ...[
Container(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () {
if (widget.pageIncrementer > 1) {
widget.pageIncrementer--;
campaignAPI(
widget.listValues["colname"]
.toString(),
widget.pageIncrementer.toString(),
searchingText, // search key
1, // flag
);
setState(() {});
} else {
util.showToast("No records to show");
}
},
child: Icon(Icons.arrow_left_rounded,
size: 50, color: Colors.white),
),
Text(
widget.pageData,
style: TextStyle(
fontSize: 15,
color: Colors.white,
fontWeight: FontWeight.bold),
),
InkWell(
onTap: () {
if (widget.totalPageCount > 1 &&
widget.totalPageCount >=
widget.pageIncrementer) {
widget.pageIncrementer++;
campaignAPI(
widget.listValues["colname"]
.toString(),
widget.pageIncrementer.toString(),
searchingText, // search key
1, // flag
);
} else {
util.showToast("No reocrds to show");
}
},
child: Icon(Icons.arrow_right_rounded,
size: 50, color: Colors.white),
),
],
),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: util.getColors(1001),
stops: [0.1, 5.0],
),
),
)
]
],
),
),
);
} else if (snapshot.hasError) {
return Text('${snapshot.error.toString()}');
}
return Center(
child: CircularProgressIndicator(),
);
},
),
),
));
}
campaignAPI(
String field_col, String page_no, String searchValue, int flag) async {
Utils util = new Utils();
SharedPreferences prefs = await SharedPreferences.getInstance();
final accessKey = prefs.getString('access_key');
final companyName = prefs.getString('company_name');
Object requestParam = {
"companyname": companyName,
"access_key": accessKey.toString(),
"field_col": field_col,
"page_no": page_no,
"searchValue": searchValue,
};
print(requestParam.toString());
APIS apis = new APIS();
Uri url = Uri.parse(apis.searchquickcreate);
util.prints(url);
util.prints(requestParam);
final response = await http.post(url, body: requestParam);
if (response.statusCode == 200) {
final body = json.decode(response.body);
print(body);
String status = body["status"];
if (status != "failed") {
if (flag == 1) {
setState(() {
widget.data = body;
});
}
return body;
} else {
util.logOut(context);
return body;
}
} else {
Navigator.pop(context);
util.showToast("Server Error !");
throw Exception("Server Error !");
}
}
}

Flutter - Image.memory not refreshing after source change

I have a page that allows users to upload documents (as images). I have structured my page in a way that for each document type that can be uploaded a Document_Upload widget is used to reduce the amount of repeated code.
On initial load I use a FutureBuilder to get all the documents the user has already uploaded from our REST Api and then populate each Document_Upload widget with the relevant data.
On successful upload our REST Api returns the new image back to the Flutter app as a Byte Array so it can be displayed.
The problem I am currently facing is that no matter what I try the image widget (Image.memory) does not display the new image, it just stays on the old one.
I have tried almost everything I can think of/ find online to resolve this issue, including:
Calling setState({}); after updating the imageString variable - I can see the widget flash but it remains on the original image.
Using a function to callback to the parent widget to rebuild the entire child widget tree - same result as setState, all the widgets flash, but no update.
Calling imageCache.clear() & imageCache.clearLiveImages() before updating the imageString.
Using CircleAvatar instead of Image.memory.
Rebuilding the Image widget by calling new Image.memory() inside the setState call.
I am starting to question if this is an issue related to Image.memory itself, however, using Image.File / Image.network is not an option with our current requirement.
Refreshing the page manually causes the new image to show up.
My code is as follows:
documents_page.dart
class DocumentsPage extends StatefulWidget {
#override
_DocumentsPageState createState() => _DocumentsPageState();
}
class _DocumentsPageState extends State<DocumentsPage>
with SingleTickerProviderStateMixin {
Future<Personal> _getUserDocuments;
Personal _documents;
#override
void didChangeDependencies() {
super.didChangeDependencies();
_getUserDocuments = sl<AccountProvider>().getUserDocuments();
}
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: SafeArea(
child: Center(
child: Padding(
padding: EdgeInsets.all(20),
child: Container(
constraints: BoxConstraints(maxWidth: 1300),
child: buildFutureBuilder(context)),
)),
),
);
}
Widget buildFutureBuilder(BuildContext context) {
var screenSize = MediaQuery.of(context).size;
return FutureBuilder<Personal>(
future: _getUserDocuments,
builder: (context, AsyncSnapshot<Personal> snapshot) {
if (!snapshot.hasData) {
return Text("Loading");
} else {
if (snapshot.data == null) {
return Center(child: Text('Error: ${snapshot.error}'));
} else {
_documents = snapshot.data;
return Column(
children: [
SizedBox(height: 20.0),
Text(
"DOCUMENTS",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: AppColors.navy),
),
Container(
constraints: BoxConstraints(maxWidth: 250),
child: Divider(
color: AppColors.darkBlue,
height: 20,
),
),
Container(
margin: EdgeInsets.only(top: 5.0, bottom: 5.0),
child: Text(
"These documents are required in order to verify you as a user",
style: TextStyle(fontSize: 14))),
Container(
margin: EdgeInsets.only(bottom: 25.0),
child: Text("View our Privacy Policy",
style: TextStyle(fontSize: 14))),
Container(
child: screenSize.width < 768
? Column(
children: [
DocumentUpload(
imageType: "ID",
imageString: _documents.id),
DocumentUpload(
imageType: "Drivers License Front",
imageString: _documents.driversLicenseFront,
),
DocumentUpload(
imageType: "Drivers License Back",
imageString: _documents.driversLicenseBack,
)
],
)
: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
DocumentUpload(
imageType: "ID",
imageString: _documents.id),
DocumentUpload(
imageType: "Drivers License Front",
imageString: _documents.driversLicenseFront,
),
DocumentUpload(
imageType: "Drivers License Back",
imageString: _documents.driversLicenseBack,
),
])),
Container(
child: screenSize.width < 768
? Container()
: Padding(
padding:
EdgeInsets.only(top: 10.0, bottom: 10.0))),
Container(
child: screenSize.width < 768
? Column(
children: [
DocumentUpload(
imageType: "Selfie",
imageString: _documents.selfie,
),
DocumentUpload(
imageType: "Proof of Residence",
imageString: _documents.proofOfResidence,
),
Container(width: 325)
],
)
: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
DocumentUpload(
imageType: "Selfie",
imageString: _documents.selfie,
),
DocumentUpload(
imageType: "Proof of Residence",
imageString: _documents.proofOfResidence,
),
Container(width: 325)
])),
],
);
}
}
});
}
}
document_upload.dart
class DocumentUpload extends StatefulWidget {
final String imageType;
final String imageString;
const DocumentUpload({this.imageType, this.imageString});
#override
_DocumentUploadState createState() => _DocumentUploadState();
}
class _DocumentUploadState extends State<DocumentUpload> {
String _imageType;
String _imageString;
bool uploadPressed = false;
Image _imageWidget;
#override
Widget build(BuildContext context) {
setState(() {
_imageType = widget.imageType;
_imageString = widget.imageString;
_imageWidget =
new Image.memory(base64Decode(_imageString), fit: BoxFit.fill);
});
return Container(
constraints: BoxConstraints(maxWidth: 325),
height: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
boxShadow: [
new BoxShadow(
color: AppColors.lightGrey,
blurRadius: 5.0,
offset: Offset(0.0, 3.0),
),
],
),
child: Card(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
child: Column(children: <Widget>[
Padding(padding: EdgeInsets.only(top: 5.0)),
Row(
//ROW 1
children: <Widget>[
Expanded(
child: Text(
_imageType,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: AppColors.darkBlue),
),
),
],
),
Row(
//ROW 2
children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.only(left: 5.0, bottom: 5.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: _imageWidget,
)),
),
Consumer<AccountProvider>(
builder: (context, provider, child) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding:
EdgeInsets.only(top: 5.0, bottom: 5.0),
child: Icon(Icons.star,
size: 20, color: AppColors.darkBlue)),
Padding(
padding:
EdgeInsets.only(top: 5.0, bottom: 5.0),
child: Text('Drag file here or',
textAlign: TextAlign.center)),
Padding(
padding:
EdgeInsets.only(top: 5.0, bottom: 5.0),
child: DynamicGreyButton(
title: uploadPressed
? "Uploading ..."
: "Browse",
onPressed: () async {
FilePickerResult result =
await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: [
'jpg',
'jpeg',
'png'
]);
if (result != null) {
uploadPressed = true;
Uint8List file =
result.files.single.bytes;
String fileType =
result.files.single.extension;
await provider
.doUploadDocument(
_imageType, file, fileType)
.then((uploadResult) {
if (uploadResult == null ||
uploadResult == '') {
showToast(
"Document failed to upload");
return;
} else {
showToast("Document uploaded",
Colors.green, "#66BB6A");
uploadPressed = false;
_imageString = uploadResult;
setState(() {});
}
});
} else {
// User canceled the picker
uploadPressed = false;
}
},
))
]));
})
],
),
])));
}
}
Image Upload HTTP Call
#override
Future uploadDocuments(DocumentsUpload model) async {
final response = await client.post(
Uri.https(appConfig.baseUrl, "/api/Account/PostDocuments_Flutter"),
body: jsonEncode(model.toJson()),
headers: <String, String>{
'Content-Type': 'application/json'
});
if (response.statusCode == 200) {
var data = json.decode(response.body);
return data;
} else {
return "";
}
}
EDIT: Attached GIF of current behaviour.
I am pretty much out of ideas at this point, any help would be greatly appreciated.
Came up with a solution.
I created a second variable to hold the new image string and showed an entirely new image widget once the second variable had value.
String _newImage;
In the success of the upload...
_newImage = uploadResult;
setState(() {});
Image widget...
child: (_newImage == null || _newImage == '')
? new Image.memory(base64Decode(_imageString), fit: BoxFit.fill)
: new Image.memory(base64Decode(_newImage), fit: BoxFit.fill)
Not a very elegant solution, but it's a solution, but also not necessarily the answer as to why the original issue was there.