Video player has error ExoPlaybackException: Source error - flutter

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,
),
),
),
),
],
);
},
),
],
),
),
),
);
}
}

Related

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

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.

Flutter: image messing up Flexible/Expanded sizes

Im trying to make something in flutter that looks like a twitter clone. The Tweet, called a wave, can be seen in the following:
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:timeago/timeago.dart' as timeago;
import '../../../../../../blocs/vote/vote_bloc.dart' as vote;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:like_button/like_button.dart';
import '../../../../../../blocs/profile/profile_bloc.dart';
import '../../../../../../blocs/wave_liking/wave_liking_bloc.dart';
import '../../../../../../models/user_model.dart';
import '../../../../../../models/wave_model.dart';
import '../../../../../../widgets/text_splitter.dart';
import '../../generic_view.dart';
import '../../photo_view/photo_view.dart';
class WaveTile extends StatelessWidget {
const WaveTile({
Key? key,
required this.poster,
required this.wave,
this.extendBelow = false,
}) : super(key: key);
final User poster;
final Wave wave;
final bool extendBelow;
#override
Widget build(BuildContext context) {
User user =
(BlocProvider.of<ProfileBloc>(context).state as ProfileLoaded).user;
return IntrinsicHeight(
child: Row(
children: [
waveColumn(context),
Expanded(
flex: 5,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
waveHeader(poster, wave, context, user),
waveText(wave, context),
if (wave.imageUrl != null) waveImage(wave, context),
waveButtons(wave),
],
))
],
),
);
}
Expanded waveColumn(BuildContext context) {
return Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 40,
height: 40,
child: InkWell(
child: Hero(
tag: 'wave${wave.id}',
child: CircleAvatar(
backgroundImage:
CachedNetworkImageProvider(poster.imageUrls[0])),
),
onTap: () {
BlocProvider.of<vote.VoteBloc>(context)
.add(vote.LoadUserEvent(user: poster));
Navigator.pushNamed(
context,
'/votes',
);
},
),
),
if (extendBelow)
Expanded(
child: VerticalDivider(
color: Color.fromARGB(255, 207, 207, 207),
thickness: 2,
width: 10,
),
),
//add a grey line
]),
);
}
}
Widget waveHeader(User poster, Wave wave, BuildContext context, User user) {
return Row(
children: [
Container(
margin: const EdgeInsets.only(right: 5.0),
child: Text(poster.name,
style: Theme.of(context)
.textTheme
.headline4!
.copyWith(fontWeight: FontWeight.bold)),
),
Text(
'${poster.handle} ยท ${timeago.format(wave.createdAt)}',
style: Theme.of(context).textTheme.subtitle1,
),
Spacer(),
WaveTilePopup(poster: poster, wave: wave, user: user),
],
);
}
Widget waveText(Wave wave, BuildContext context) {
return Flexible(
child: TextSplitter(
wave.message,
context,
Theme.of(context).textTheme.subtitle2!,
));
}
Widget waveImage(Wave wave, BuildContext context) {
return Flexible(
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: InkWell(
child: CachedNetworkImage(
imageUrl: wave.imageUrl!,
fit: BoxFit.fill,
),
onTap: () {
Navigator.pushNamed(
context,
MyPhotoView.routeName,
arguments: {
'imageUrls': [wave.imageUrl!],
'index': 0
},
);
},
),
),
);
}
Widget waveButtons(Wave wave) {
return BlocBuilder<ProfileBloc, ProfileState>(
builder: (context, profileState) {
if (profileState is ProfileLoading) {
return Container();
}
if (profileState is ProfileLoaded) {
User user = profileState.user;
return Container(
margin: const EdgeInsets.only(top: 10.0, right: 20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Row(
children: [
//font awesome comment icon
Icon(
FontAwesomeIcons.comment,
size: 14.0,
color: Colors.grey,
),
SizedBox(width: 5),
Text(wave.comments.toString()),
],
),
BlocBuilder<WaveLikingBloc, WaveLikingState>(
builder: (context, waveLikingState) {
//set waveLikingState to be WaveLikingLoaded
waveLikingState = waveLikingState as WaveLikingLoaded;
bool inShortTermLikes =
waveLikingState.shortTermLikes.contains(wave.id);
bool inShortTermDislikes =
waveLikingState.shortTermDislikes.contains(wave.id);
bool inLikes = waveLikingState.likes.contains(wave.id);
bool inDislikes = waveLikingState.dislikes.contains(wave.id);
bool likedBy = wave.likedBy.contains(user.id);
bool isLiked = ((likedBy || inLikes || inShortTermLikes) &&
!(inDislikes || inShortTermDislikes));
int likeCount = (inLikes || inShortTermLikes)
? wave.likes + 1
: (inDislikes || inShortTermDislikes)
? wave.likes - 1
: wave.likes;
return LikeButton(
isLiked: isLiked,
size: 30,
circleColor: CircleColor(
start: Colors.red[300]!, end: Colors.red[900]!),
bubblesColor: BubblesColor(
dotPrimaryColor: Colors.red[300]!,
dotSecondaryColor: Colors.red[900]!,
),
likeBuilder: (bool isLiked) {
return Icon(
Icons.favorite,
color: isLiked ? Colors.red[900] : Colors.grey,
size: 20,
);
},
likeCount: likeCount,
countBuilder: (int? count, bool isLiked, String text) {
var color = isLiked ? Colors.red[900] : Colors.grey;
Widget result;
result = Text(
text,
style: TextStyle(color: color),
);
return result;
},
onTap: (bool isLiked) async {
(isLiked)
? BlocProvider.of<WaveLikingBloc>(context).add(
DislikeWave(waveId: wave.id, userId: user.id!))
: BlocProvider.of<WaveLikingBloc>(context)
.add(LikeWave(waveId: wave.id, userId: user.id!));
return !isLiked;
},
);
},
),
],
),
);
}
return Container();
},
);
}
When creating a wave with just text, it will end up looking like this:
This is the ideal situation right now. However, when an image is added, it looks like this:
Going through widget inspector does not seem to help me much, and the only way I can change the size of the wave is by deleting the image, leading me to believe the image is causing some weird interaction with the Flexible/Expanded widgets near it. Anyone got any ideas?
Thanks!
As you have 2 Flexible in a column , it will take up all the space. You can try removing Flexible from Text.

Update State without reloading a widget in Flutter

I have a widget on a screen that receives its data from API calls. The API call is made inside the init method of the Navigation Bar so that continuous API calls can be prevented when going back and forth between screens. Although this works fine, I'm facing a real challenge in trying to get the state of the widget updated when new data is added to that particular API that the widget relies on for displaying data. I would therefore need to know how to display the updated data that I added to the Database by making a post request on a different screen. The only way this happens now is by way of reloading the entire app or by killing it. Any help will be appreciated.
This is the NavBar where the API is getting called. I usually make all the API calls at once here and something I have done here too.
NavBar
class CustomBottomNavigationState extends State<CustomBottomNavigation> {
bool isLoading = true;
int index = 2;
final screens = [
MenuScreen(),
LeaveScreen(),
// TaskList(),
HomeScreen(),
// PaySlipScreen(),
TaskList(),
Claimz_category(),
// ClaimzScreen()
];
#override
void initState() {
// TODO: implement initState
Provider.of<LeaveRequestViewModel>(context, listen: false)
.getLeaveRequest()
.then((value) {
Provider.of<AnnouncementViewModel>(context, listen: false)
.getAllAnouncements()
.then((value) {
Provider.of<TodaysTaskList>(context, listen: false)
.getTodaysTasks() //This is the API call in question
.then((value) {
setState(() {
isLoading = false;
});
});
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
final items = ['The icons are stored here'];
// TODO: implement build
return SafeArea(
child: Scaffold(
body: isLoading
? const Center(
child: CircularProgressIndicator(),
)
: screens[index],
extendBody: true,
bottomNavigationBar: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(200),
topRight: Radius.circular(200)),
boxShadow: [
BoxShadow(
color: Colors.transparent,
blurRadius: 10,
offset: Offset(1, 2))
]),
child: CurvedNavigationBar(
items: items,
index: index,
height: 60,
color: const Color.fromARGB(255, 70, 70, 70),
backgroundColor: Colors.transparent,
onTap: (index) => setState(() {
this.index = index;
})),
),
),
);
}
}
ToDoList widget(This the widget where the updates never reflect without reloading)
class ToDoListState extends State<ToDoList> {
#override
Widget build(BuildContext context) {
final toDoList = Provider.of<TodaysTaskList>(context).getToDoList; //This is the getter method that stores the data after it has been fetched from API
// TODO: implement build
return ContainerStyle(
height: SizeVariables.getHeight(context) * 0.35,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.only(
top: SizeVariables.getHeight(context) * 0.015,
left: SizeVariables.getWidth(context) * 0.04),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
// color: Colors.red,
child: FittedBox(
fit: BoxFit.contain,
child: Text(
'To do list',
style: Theme.of(context).textTheme.caption,
),
),
),
],
),
),
SizedBox(height: SizeVariables.getHeight(context) * 0.01),
Padding(
padding: EdgeInsets.only(
left: SizeVariables.getWidth(context) * 0.04,
top: SizeVariables.getHeight(context) * 0.005,
right: SizeVariables.getWidth(context) * 0.04),
child: SizedBox(
height: SizeVariables.getHeight(context) * 0.25,
child: Container(
// color: Colors.red,
child: toDoList['today'].isEmpty
? Center(
child: Lottie.asset('assets/json/ToDo.json'),
)
: ListView.separated(
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) => Row(
children: [
Icon(Icons.circle,
color: Colors.white,
size:
SizeVariables.getWidth(context) * 0.03),
SizedBox(
width:
SizeVariables.getWidth(context) * 0.02),
FittedBox(
fit: BoxFit.contain,
child: Text(
toDoList['today'][index]['task_name'], //This is where it is used
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.bodyText1),
)
],
),
separatorBuilder: (context, index) => Divider(
height: SizeVariables.getHeight(context) * 0.045,
color: Colors.white,
thickness: 0.5,
),
itemCount: toDoList['today'].length > 4
? 4
: toDoList['today'].length),
),
),
)
],
),
);
}
}
The other widget where the date gets added
class _TaskListState extends State<TaskList> {
#override
Widget build(BuildContext context) {
var floatingActionButton;
return Scaffold(
backgroundColor: Colors.black,
floatingActionButton: Container(
....
....,
child: FloatingActionButton(
backgroundColor: Color.fromARGB(255, 70, 69, 69),
onPressed: openDialog, //This is the method for posting data
child: Icon(Icons.add),
),
),
),
body: Container(
....
....
....
),
);
}
Future<dynamic> openDialog() => showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Color.fromARGB(255, 87, 83, 83),
content: Form(
key: _key,
child: TextFormField(
controller: taskController,
maxLines: 5,
style: Theme.of(context).textTheme.bodyText1,
decoration: InputDecoration(
border: InputBorder.none,
),
validator: (value) {
if (value!.isEmpty || value == '') {
return 'Please Enter Task';
} else {
input = value;
}
},
),
),
actions: [
InkWell(
onTap: () async {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2010),
lastDate:
DateTime.now().add(const Duration(days: 365)))
.then((date) {
setState(() {
_dateTime = date;
});
print('Date Time: ${dateFormat.format(_dateTime!)}');
});
},
child: const Icon(Icons.calendar_month, color: Colors.white)),
TextButton(
child: Text(
"Add",
style: Theme.of(context).textTheme.bodyText1,
),
onPressed: () async {
Map<String, dynamic> _data = {
'task': taskController.text,
'task_date': dateFormat.format(_dateTime!).toString()
};
print(_data);
if (_key.currentState!.validate()) {
await Provider.of<ToDoViewModel>(context, listen: false)
.addToDo(_data, context) //This is the post method
.then((_) {
Navigator.of(context).pop();
Provider.of<TodaysTaskList>(context, listen: false)
.getTodaysTasks(); //I did this here again to re-initialize the data. I was under the impression that the new data would get initialized for the widget to reflect it on the other screen.
});
}
},
),
],
),
);
void add() {
Navigator.of(context).pop();
}
}
The Get API Call
class TodaysTaskList with ChangeNotifier {
Map<String, dynamic> _getToDoList = {};
Map<String, dynamic> get getToDoList {
return {..._getToDoList};
}
Future<void> getTodaysTasks() async {
SharedPreferences localStorage = await SharedPreferences.getInstance();
var response = await http.get(Uri.parse(AppUrl.toDoList), headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ${localStorage.getString('token')}'
});
if (response.statusCode == 200) {
_getToDoList = json.decode(response.body);
} else {
_getToDoList = {};
}
print('TO DO LIST: $_getToDoList');
notifyListeners();
}
}
Please let me know for additional input.
i think it's because you didn't call the provider to update your state correctly
as i see that you declare new variable to store your provider like this
final toDoList = Provider.of<TodaysTaskList>(context).getToDoList;
then you use it like this
Text(
toDoList['today'][index]['task_name'], //This is where it is used
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.bodyText1),
)
it's not updating the state, you should wrap the widget that need to be updated with Consumer
Consumer<TodaysTaskList>(
builder: (context, data, child) {
return _Text(
data.[your_list]['today'][index]['task_name'],
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodyText1),
);
},
);

flutter infinite scrolling data from server in listview builder

I am using graphql_flutter to load data from the server and I should update moreId for the update page in my server and get more data to load, and I need to use infinite for it.
How can I do it?
class MoreEpd extends StatefulWidget {
final String moreId;
const MoreEpd({Key? key, required this.moreId}) : super(key: key);
#override
_MoreEpdState createState() => _MoreEpdState();
}
class _MoreEpdState extends State<MoreEpd> {
double pageWidth = 0;
double pageHeigh = 0;
int pageNum = 0;
final String leftArrow = 'assets/icons/left-arrow.svg';
String getSearchResult = """
query homeview(\$moreId: ID!, \$page: Int! ){
homeview(HM_ID: \$moreId, page: \$page){
HM_ID
HM_Type_ID
HM_Type_Name
HM_NAME
Priority
Details{
HM_Det_ID
HM_ID
Ep_ID
Pod_ID
Link
Image
title
Pod_title
}
}
}
""";
#override
Widget build(BuildContext context) {
pageWidth = MediaQuery.of(context).size.width;
pageHeigh = MediaQuery.of(context).size.height;
return Container(
child: Query(
options: QueryOptions(
document: gql(getSearchResult),
variables: {'moreId': widget.moreId, 'page': pageNum},
),
builder: (
QueryResult result, {
Refetch? refetch,
FetchMore? fetchMore,
}) {
return handleResult(result);
},
),
);
}
Widget handleResult(QueryResult result) {
var data = result.data!['homeview']['Details'] ?? [];
return Container(
child: ListView.builder(
padding: EdgeInsets.only(top: 15),
shrinkWrap: true,
itemCount: data.length ,
itemBuilder: (context, index) {
return InkWell(
onTap: () {},
child: Padding(
padding: EdgeInsets.only(
top: pageWidth * 0.0,
right: pageWidth * 0.08,
left: pageWidth * 0.08,
bottom: pageWidth * 0.0),
child: Container(
child: Stack(
children: [
Column(
children: [
Padding(
padding:
EdgeInsets.only(bottom: pageWidth * 0.060),
child: Row(
children: [
Padding(
padding:
EdgeInsets.only(left: pageWidth * 0.01),
child: Container(
// alignment: Alignment.centerRight,
width: pageWidth * 0.128,
height: pageWidth * 0.128,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: CachedNetworkImageProvider(
data[index]['Image'],
)),
borderRadius: BorderRadius.all(
Radius.circular(15)),
// color: Colors.redAccent,
border: Border.all(
color: MyColors.lightGrey,
width: 1,
)),
),
),
Expanded(
child: Row(
children: [
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Container(
width: pageWidth * 0.5,
alignment: Alignment.centerRight,
child: Text(
data[index]['title'],
textAlign: TextAlign.right,
overflow: TextOverflow.ellipsis,
maxLines: 1,
// softWrap: true,
style: TextStyle(
// fontWeight: FontWeight.bold,
fontSize: 14,
),
),
),
],
),
],
),
)
],
),
),
],
),
],
),
),
),
);
}));
}
}
First error is happening because of not handling the states of Query. In order to do that on builder:
delearing data on state level var data = [];
builder: (
QueryResult result, {
Refetch? refetch,
FetchMore? fetchMore,
}) {
if (result.hasException) {
return Text(result.exception.toString());
}
if (result.isLoading) {
return Column(
children: [
Expanded(
child: handleResult(data)), // show data while loading
const Center(
child: CircularProgressIndicator(),
),
],
);
}
data.addAll(result.data!['homeview']['Details'] ?? []);
return handleResult(data);
},
All we need now to increase the pageNum to get more data. I'm using load more button, better will be creating the load button end of list by increasing itemLength+=1.
Update using ScrollController.
// on state class
final ScrollController controller = ScrollController();
bool isLoading = false;
Load data on scroll
#override
void initState() {
super.initState();
controller.addListener(() {
/// load date at when scroll reached -100
if (controller.position.pixels >
controller.position.maxScrollExtent - 100) {
print("Scroll on loading");
if (!isLoading) {
print("fetching");
setState(() {
pageNum++;
isLoading = true;
});
}
}
});
}
Full Snippet on Gist
And about the position issue, you can check this

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.