My flutter app was running fine on all platforms for months and has randomly started crashing and I cannot find the issue or figure why it started happening out of the blue. The crash closes the app and the app is unable to open again.
I have both Crashlytics and Sentry set up to check crash logs but neither are showing what the issue is. I was only able to pull the following errors by reproducing the crash with a real device connected to VSCode. I have provided two, the error almost always occurs on a specific screen or on the screen before that one. The error occurs on some android devices, but never on Samsung Galaxy S21 or S8 which I have personally tested on. On iPhone 6, The app crashes way before I can get to the screens in question. The error does not happen on the emulator. Once it crashes and stops on iPhone 12, the app will not even launch on the phone if I try opening it.
I have tried updating Flutter and XCode, using CachedNetworkImage instead of just NetworkImage, and I have ensured that I call if(mounted) before any calls to setState to minimise any chance of a memory leak, and that I properly dispose of any StreamSubscriptions by overriding the dispose() method. I don't even know where to start looking for this issue.
Please help me identify what is going on here. What method can I use to find what is causing this crash??
Running on iPhone 12:
Error 1:
* thread #15, name = 'io.worker.3', stop reason = EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=2098 MB, unused=0x0)
frame #0: 0x000000010f011538 Flutter`ycc_rgb_convert + 140
Flutter`ycc_rgb_convert:
-> 0x10f011538 <+140>: strb w21, [x5]
0x10f01153c <+144>: ldr x21, [x12, x19, lsl #3]
0x10f011540 <+148>: ldr x20, [x11, x20, lsl #3]
0x10f011544 <+152>: add x20, x20, x21
Target 0: (Runner) stopped.
Lost connection to device.
Error 2:
[Process] 0x11f000d30 - NetworkProcessProxy::didClose (Network Process 0 crash)
[ServicesDaemonManager] interruptionHandler is called. -[FontServicesDaemonManager connection]_block_invoke
* thread #15, name = 'io.worker.3', stop reason = EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=2098 MB, unused=0x0)
frame #0: 0x000000011336f7ac Flutter`ycc_rgb_convert + 140
Flutter`ycc_rgb_convert:
-> 0x11336f7ac <+140>: strb w21, [x5]
0x11336f7b0 <+144>: ldr x21, [x12, x19, lsl #3]
0x11336f7b4 <+148>: ldr x20, [x11, x20, lsl #3]
0x11336f7b8 <+152>: add x20, x20, x21
Target 0: (Runner) stopped.
Lost connection to device.
Flutter Version:
Flutter 3.7.0 β’ channel stable β’ https://github.com/flutter/flutter.git
Framework β’ revision b06b8b2710 (2 days ago) β’ 2023-01-23 16:55:55 -0800
Engine β’ revision b24591ed32
Tools β’ Dart 2.19.0 β’ DevTools 2.20.1
Running flutter doctor...
Doctor summary (to see all details, run flutter doctor -v):
[β] Flutter (Channel stable, 3.7.0, on macOS 12.6.3 21G419 darwin-x64, locale en-GB)
[β] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
[β] Xcode - develop for iOS and macOS (Xcode 14.2)
[β] Chrome - develop for the web
[β] Android Studio (version 2021.2)
[β] VS Code (version 1.74.3)
[β] Connected device (3 available)
[β] HTTP Host Availability
β’ No issues found!
EDIT:
Thanks for your reply. The crash mostly happens in one specific part of the app where I'm loading the images in a screen (iPhone 6) or one or two screens after the images screen is loaded (iPhone12). I upgraded to Flutter 3.7 as an attempt to fix the issue which still persists.
I think it's an image issue because ChatGPT said: "The function name "ycc_rgb_convert" suggests that it could be related to image processing, as YCC (YUV) is a color space used in image and video compression, but it doesn't confirm that it is related to loading images." Of course, this is merely a clue and not definitive.
It also does not happen on all devices. It happens on iPhone 6, it takes a bit longer to happen on iPhone 12, and it doesn't happen at all on either Samsung Galaxy S21 or S8. The error started happening about 2 weeks ago, and I only started using CacheNetworkImage while trying to fix this issue, I didn't need it before and its not really helped now to be honest.
The crash happens in the places screen (or shortly after this screen is in the stack) where I load a sliver grid of "place items". A place item is a grid tile that has a main image and an avatar image, as well as some text and 2 icon buttons from the package like_button placed in the header and footer of the Grid tile. Clicking on a place item takes you to the place detail screen, where the crash will sometimes happen with iPhone12. If I totally avoid the places screen on iPhone 6, the crash will not happen. But if I go load even a measly 4 place items in the places screen, the crash will happen on iPhone 6.
I paginate by loading 6 place items initially and then an additional 4 as the user scrolls. The main images I'm loading are largest and are no more than 1MB ( they have an average size of maybe 400-500kb) and the avatar images are even smaller, the biggest being maybe 200kb and the smallest being less than 20kb. The images are the same ones they have always been as they belong to place documents.
The places screen has a background image loaded from assets that is 200kb and is also precached (another thing I did to fix this but wasn't there or necessary before). I also use the same placeholder image for each of the cached network images I'm loading, and this is also 200kb and is precached.
Could you perhaps give pointers on how I can identify the memory leak? Trying to use memory profiler in DevTools is not working because it auto disconnects when the crash happens and I get nothing from the console except the error I gave above (only with iPhone 12, no logs at all from iPhone 6). When I ran it on galaxy s8 in profile mode, the retained size never exceeded 25MB while I took all steps that would cause the crash in iPhone 6 and 12, and it runs smooth as butter. No memory spikes.
When I run the iPhone 6 in profile mode, Before it disconnects, the dart heap stays around 16MB, retained size stays around 20-30MB from starting dev tools until the crash, there are no visible memory spikes but perhaps I'm using the tool incorrectly? Idk...
Sentry.io logs these iPhone 6 crashes and says: "OutOfMemory: The OS most likely terminated your app because it overused RAM." but it gives no further details.
Sentry is also a new addition in trying to diagnose this issue.
Here is the place item code:
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:like_button/like_button.dart';
import 'package:provider/provider.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cached_network_image/cached_network_image.dart';
import '../data/color_palette.dart';
import '../widgets/pricing_icons.dart';
import '../models/place.dart';
import '../screens/place_detail_screen.dart';
import '../models/user.dart';
import '../services/database.dart';
import './custom_dialogbox.dart';
class PlaceItem extends StatelessWidget {
final Place venue;
PlaceItem(this.venue);
final FirebaseAuth _auth = FirebaseAuth.instance;
final DatabaseService _db = DatabaseService();
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
final venue = Provider.of<Place>(context, listen: false);
return ClipRRect(
clipBehavior: Clip.antiAlias,
borderRadius: BorderRadius.circular(20),
child: GestureDetector(
onTap: () {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
Navigator.of(context).pushNamed(
PlaceDetailScreen.routeName,
arguments: venue.venueId,
// Extract in the screen we'll nav to with ModalRoute
);
},
child: GridTile(
header: Consumer<Place>(
builder: (ctx, venue, child) => _gridTileHeader(context)),
footer: _gridTileFooter(size),
// Venue Image
child: CachedNetworkImage(
imageUrl: venue.venueImageUrl,
fadeInDuration: const Duration(milliseconds: 500),
placeholder: (context, url) => Image.asset(
'assets/images/tile_background.png',
fit: BoxFit.cover,
),
fit: BoxFit.cover,
errorWidget: (context, url, error) => Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/images/tile_background.png',
),
fit: BoxFit.cover,
),
),
child: const Center(
child: Icon(Icons.error, color: Colors.grey,),
),
),
),
),
),
);
}
Widget _gridTileHeader(context) {
final user = _auth.currentUser;
final animationDuration = const Duration(milliseconds: 700);
return StreamBuilder<UserData>(
stream: DatabaseService(uid: user.uid).userData,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserData userData = snapshot.data;
bool isBookmarked = userData.bookmarks.contains(venue.venueId);
bool isFaved = userData.favorites.contains(venue.venueId);
return GridTileBar(
backgroundColor: Colors.black.withOpacity(0.4),
// Animated bookmark button
leading: LikeButton(
// key: _globalKey,
isLiked: isBookmarked,
circleColor: const CircleColor(
start: firstColor,
end: sixthColor,
),
bubblesColor: const BubblesColor(
dotPrimaryColor: sixthColor,
dotSecondaryColor: Colors.red,
),
// size: 100, // used when testing for closer look
// size: 50, // size of containing circle
likeBuilder: (bool bookmarked) {
return Icon(
Icons.bookmark,
color: isBookmarked ? sixthColor : Colors.white,
// size: 75, // used when testing for closer look
size: 20,
);
},
animationDuration: animationDuration,
onTap: (bookmarked) async {
print(bookmarked);
print(isBookmarked);
// return !bookmarked;
try {
// // if user's email isn't verified
if (!user.emailVerified) {
showDialog(
context: context, builder: (ctx) => VerifyEmail());
return bookmarked; // do nothing
} else {
// actual functionality
DatabaseService(uid: user.uid)
.toggleToUserList('bookmarks', venue.venueId);
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
elevation: 10,
backgroundColor: firstColor,
content: !isBookmarked
? const Text('Venue Added to Bookmarks!')
: const Text('Venue Removed from Bookmarks'),
duration: const Duration(milliseconds: 2500),
action: SnackBarAction(
textColor: sixthColor,
label: 'UNDO',
onPressed: () async {
// widget.venue.toggleBookmarkStatus();
DatabaseService(uid: user.uid)
.toggleToUserList('bookmarks', venue.venueId);
// reverse it
_db.updateLikesBookmarks(
venue.venueId,
venue.vendorId,
venue.vendorName,
venue.address['city'],
'bookmarks',
bookmarked,
);
},
),
),
);
// Track bookmarks
// putting "await" with the function stops the animation
_db.updateLikesBookmarks(
venue.venueId,
venue.vendorId,
venue.vendorName,
venue.address['city'],
'bookmarks',
!bookmarked,
);
return !bookmarked;
}
} catch (e) {
print(e.toString());
return bookmarked; // dont change it if there was an error
}
},
),
title: venue.ownerManaged
? const Icon(
Icons.verified,
color: Colors.white,
)
: const Text(''),
// Animated Favorites Button
trailing: LikeButton(
// key: _globalKey,
isLiked: isFaved,
circleColor: const CircleColor(
start: firstColor,
end: Colors.red,
),
bubblesColor: const BubblesColor(
dotPrimaryColor: secondColor,
dotSecondaryColor: Colors.red,
),
// size: 100, // used when testing for closer look
// size: 50, // sizze of containing circle
likeBuilder: (bool faved) {
return Icon(
Icons.favorite,
color: isFaved ? secondColor : Colors.white,
// size: 75, // used when testing for closer look
size: 20,
);
},
animationDuration: animationDuration,
onTap: (faved) async {
// print(faved);
// print(isFaved);
// return !faved;
try {
// // if user's email isn't verified
if (!user.emailVerified) {
showDialog(
context: context, builder: (ctx) => VerifyEmail());
return faved; // do nothing
} else {
// actual functionality
DatabaseService(uid: user.uid)
.toggleToUserList('favorites', venue.venueId);
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
// behavior: SnackBarBehavior.floating,
elevation: 10,
backgroundColor: firstColor,
content: !isFaved
? const Text('Venue Added to Favorites!')
: const Text('Venue Removed from Favorites'),
duration: const Duration(milliseconds: 2500),
action: SnackBarAction(
textColor: sixthColor,
label: 'UNDO',
onPressed: () async {
// widget.venue.toggleBookmarkStatus();
DatabaseService(uid: user.uid)
.toggleToUserList('favorites', venue.venueId);
// reverse it
_db.updateLikesBookmarks(
venue.venueId,
venue.vendorId,
venue.vendorName,
venue.address['city'],
'likes',
faved,
);
},
),
),
);
// Track bookmarks
// putting "await" with the function stops the animation
_db.updateLikesBookmarks(
venue.venueId,
venue.vendorId,
venue.vendorName,
venue.address['city'],
'likes',
!faved,
);
return !faved;
}
} catch (e) {
print(e.toString());
return faved; // dont change it if there was an error
}
},
),
);
} else if (user.isAnonymous) {
return GridTileBar(
backgroundColor: Colors.black.withOpacity(0.35),
leading: IconButton(
icon: const Icon(
Icons.bookmark_border,
color: Colors.white,
size: 20,
),
onPressed: () {
showDialog(context: context, builder: (ctx) => AnonDialog());
},
),
title: const Text(''),
trailing: IconButton(
icon: const Icon(
Icons.favorite_border,
size: 20,
),
onPressed: () {
showDialog(context: context, builder: (ctx) => AnonDialog());
},
),
);
} else {
// fake tile bar with no functionality
return GridTileBar(
backgroundColor: Colors.black.withOpacity(0.35),
leading: IconButton(
icon: const Icon(
Icons.bookmark_border,
color: Colors.white,
size: 20,
),
onPressed: () {},
),
title: const Text(''),
trailing: IconButton(
icon: const Icon(
Icons.favorite_border,
size: 20,
),
onPressed: () {},
),
);
}
});
}
Widget _gridTileFooter(size) {
return GridTileBar(
backgroundColor: Colors.black87,
// title
title: Padding(
padding: const EdgeInsets.only(top: 1.0, bottom: 3),
child: Text(
venue.venueName,
overflow: TextOverflow.ellipsis,
softWrap: true,
maxLines: 2,
textAlign: TextAlign.left,
style: const TextStyle(
color: Colors.white,
fontFamily: 'Poppins',
fontSize: 12,
height: 1),
),
),
// subtitle column
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// price icons
FittedBox(
fit: BoxFit.fitWidth,
child: PricingIconsDB(venue),
),
// Google rating
Container(
padding: const EdgeInsets.only(
left: 2,
top: 2,
),
width: size.width * 0.25,
child: FittedBox(
fit: BoxFit.fitWidth,
child: Row(
children: [
const Icon(
FontAwesomeIcons.google,
color: Colors.white70,
// color: firstColor,
size: 10,
),
const Text(' Rating: '),
Text(
venue.googleRating == null
? '-'
: venue.googleRating
.toStringAsFixed(1)
.replaceAll(RegExp(r"([.]*0)(?!.*\d)"), ""),
// '4.1',
style: const TextStyle(color: firstColor),
),
const Icon(
Icons.star,
// color: Colors.white,
color: firstColor,
size: 15,
),
],
),
),
),
],
),
trailing: Container(
height: size.width * 0.126,
width: size.width * 0.126,
clipBehavior: Clip.antiAlias,
decoration: const BoxDecoration(
shape: BoxShape.circle,
),
child: venue.vendorLogoUrl == null || venue.vendorLogoUrl.isEmpty
? Container(
color: Colors.black26,
)
// What I used as placeholders before the crashes started:
// The gifs may be expensive in terms of RAM - 200KB - 500KB each
// ? Image.asset(
// // restaurant or cafe
// // loadedVenue
// // .businessCategories
// // .contains('c2')
// venue.venueCategories["restaurant"]
// ? 'assets/images/food_logo.gif'
// :
// // bar or pub
// // loadedVenue
// // .businessCategories
// // .contains('c3')
// venue.venueCategories["barOrPub"]
// ? 'assets/images/cocktail_logo.gif'
// : venue.venueCategories["cafe"]
// ? 'assets/images/cafe_logo.gif'
// :
// // cinema
// 'assets/images/cinema_logo.gif')
: CachedNetworkImage(
imageUrl: venue.vendorLogoUrl,
placeholder: (context, url) =>
Container(color: Colors.black26),
errorWidget: (context, url, error) => const Icon(Icons.error),
fadeInDuration: const Duration(milliseconds: 700),
)
// What I used before incorporating CachedNetworkImage for this crash
// : FadeInImage.assetNetwork(
// placeholder: venue.venueCategories["restaurant"]
// ? 'assets/images/food_logo.gif'
// :
// // bar or pub
// // loadedVenue
// // .businessCategories
// // .contains('c3')
// venue.venueCategories["barOrPub"]
// ? 'assets/images/cocktail_logo.gif'
// : venue.venueCategories["cafe"]
// ? 'assets/images/cafe_logo.gif'
// :
// // cinema
// 'assets/images/cinema_logo.gif',
// image: venue.vendorLogoUrl,
// fit: BoxFit.fitHeight,
// ),
),
);
}
}
And here is the Sliver Grid that uses these PlaceItems:
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: constraints.maxWidth > 700 ? 3 : 2,
mainAxisSpacing: size.width * 0.025,
crossAxisSpacing: size.width * 0.025,
childAspectRatio: 1,
),
delegate: SliverChildBuilderDelegate(
(ctx, i) => ChangeNotifierProvider.value(
value: _places[i],
child: PlaceItem(_places[i]),
),
childCount: _places.length,
),
),
It lives inside a CustomScrollView as one of the slivers.
Just as you would expect, the crash is related to images.
I struggled with the same issue you have when I was building an app that uses a lot of images.
The crash can happen when you are out of memory (a lot of live images) and/or you have many https requests running at the same time (downloading images).
You can clear the image cache and then do fast scrolling on the grid containing images to reproduce this issue.
The solution could be:
Use a lower image resolution, or perhaps resize the image before displaying it.
Alternatively, if you want to use high-resolution images, you can try to dispose the images manually by calling evict method on the ImageProvider you are using, like in the example below.
final imageProvider = NetworkImage(bytes);
imageProvider.evict();
However, you need to make sure that the image is fully loaded before calling evict or it will have no effect.
Be sure not to download too many images at the same time, this could be tricky. One possibility is to manually download images in a StatefulWidget and then cancel the download in the dispose method.Β
I've already developed a package to solve this exact issue disposable_cached_images. Give it a try, and if it solve the issue you can use it or modify the source code to get the desired behaviour.
I've had issues scrolling through lists with images too. My issue was when they were local to the device also, but very similar to what you're describing.
I fixed my issue by resizing the images in memory, instead of loading it full size, and letting the constraints of the UI render the full size image down to its container.
CachedNetworkImage has some params to minimize memory impact. Perhaps these would help you (depending on how you prefer to size the image):
memCacheWidth
memCacheHeight
Usage:
CachedNetworkImage(
memCacheWidth: 100,
imageUrl: venue.venueImageUrl,
// ...
)
#Marvioso have you opened a github issue? .. In Flutter repo. This looks like something that needs to be known by the Flutter team. It does look like a Flutter or iOS issue.. I've started the app and left it as.. without scrolling or navigating and it still runs out of memory.
I'm having a problem in flutter on vs code
I imported the audioplayers
here's my pubspec.yaml
here's my homepage where I call the audio players
import 'package:flutter/material.dart';
import 'package:audioplayers/audioplayers.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.brown,
title: Text('anghami'),
),
body: Container(
color: Colors.brown[200],
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Card(
child: ListTile(
title: Text(
"benab",
style: TextStyle(color: Colors.red),
),
//tileColor: Colors.red,
leading: Icon(Icons.music_note),
iconColor: Colors.red,
onTap: () async {
final player = AudioPlayer();
await player
.setSource(AssetSource('assets/music/music1.mp3'));
},
),
),
],
),
),
),
);
}
}
whenever i try to play the music from the phone I get this error
P.S: the vs code has no problem in loading images or using other type of assets .
i've tried using Audiocache it doesn't work especially they deleted it in the last version ^1.1.1 ,
[enter image description here][https://i.stack.imgur.com/u9kKR.png]
It seems you trying to load an asset in /assets/assets/music/ folder.
My guess is that you want to load an asset in /assets/music/ folder and it's a simple mistake.
To fix that:
// Replace the relative path of your asset below:
// assets/music/music1.mp3 -> music/music1.mp3
await player.setSource(AssetSource('music/music1.mp3'));
Just remove assets from your audio source like this:
await player.setSource(AssetSource('music/music1.mp3'));
Firstly, create a assets directory on the root of your project , then create music folder.
Try to play with
await player.setSource(AssetSource('music/music1.mp3'));
Hello guys the solutions here are useful , so the main problem that I had was in the path so after correction it loaded the asset in a normal way,
but instead of just loading the asset you want it to play obviously and that's guaranteed by using :
**final player = AudioPlayer();**
**await player.play(AssetSource('music/music1.mp3'));**
Listen my problem in this short video.
I've tested this case by using some other audio packages(ttsγtext to speech), but the result is the same, is this problem caused by the Android studio emulator?
I've tried reinstalling, changing every device and API, and changing the audio buffer size,but nothing solves the problem in android studio emulator noise.
In addition, I changed the emulator to bluestacks 5 and genymotion, but it became no sound
question
1.How can I fix android studio emulator noise problem in windows 10?
2.Why can't bluestacks 5 and genymotion play sound
import 'package:flutter/material.dart';
import 'package:flutter_tts/flutter_tts.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key? key}) : super(key: key);
final FlutterTts flutterTts = FlutterTts();
speak() async{
//print(await flutterTts.setPitch);
await flutterTts.setLanguage("en-US");
//await flutterTts.setPitch(1.0);
//await flutterTts.speak(text);
await flutterTts.speak('hello how are you');
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('tts'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'Press this button to say hello',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20),
),
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () async{
speak();
},
child: Column(
children: <Widget>[
Text('say hello',style: TextStyle(
fontSize: 20,
color: Colors.white,
),
)
],
),
)
],
),
)
],
),
);
}
}
I suggest testing the following on your current emulator setup, a real device, and genymotion/stacks emulator (all three things):
Does ANY audio play properly such as a youtube video, or sound settings demo?
If no, then STOP HERE: the problem has nothing to do with your code -- it has to do with the device, or in the case of an emulator, a bug with the way the emulator is using the audio hardware of the host device.
Does the tts engine demo test work properly in the devices settings?
If no, then STOP HERE: the problem is with the tts engine on the device.
Does your app work properly?
If no, then the problem could be your code. (but if you've already passed stages 1 and 2, then I very much doubt this will fail).
PS - If your app works on a real device then you really don't have a problem.
The idea is to have a sticky header to work with a GridView. I want the user to see a sticky header at the top of their screen when they are scrolling down the GridView.
Getting the "Null check operator used on a null value" error when the BlocBuilder creates the widgets when the if-clause statement if (state.isAvatarFetchSuccess) is equal to true. Basically _stickyHeader.render() just returns a StickyHeader from here. The console outputs that the error specifically happens on the return BlocBuilder<AvatarSettingsBloc, AvatarSettingsState>( line.
I tried using StickyHeader directly as a widget, without using the abstract factory desing pattern, but it did not solve the problem. I debugged whether the data that is being used, the List that have the data to be generated into the necessary widgets by the GridView, but I can verify that the List is not empty. I tried using ListView, produced the same error. I googled the error message expecting to find a solution, but it seems like this is an "abstract" issue, and not something that happens in a combination of certain widgets, as far as I understand.
Does this have something to do with the new "Null Safety" update? Current Flutter version is 2.0.5. Here is a dump from flutter doctor.
[β] Flutter (Channel stable, 2.0.5, on Microsoft Windows [Version 10.0.19042.928], locale en-US)
[!] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
X Android license status unknown.
Run `flutter doctor --android-licenses` to accept the SDK licenses.
See https://flutter.dev/docs/get-started/install/windows#android-setup for more details.
[β] Chrome - develop for the web
[β] Android Studio (version 4.1.0)
[β] VS Code (version 1.55.2)
[β] Connected device (3 available)
#override
Widget build(BuildContext context) {
return BlocBuilder<AvatarSettingsBloc, AvatarSettingsState>(
builder: (context, state) {
if (state.isAvatarFetchSuccess) {
print('avatar_settings_page avatarUrlList: ${state.avatarUrlList}');
return _stickyHeader.render(
headerChild: Text('test'),
content: GridView.builder(
shrinkWrap: true,
physics: ScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 5.0,
mainAxisSpacing: 5.0,
),
itemCount: state.avatarUrlList.length,
itemBuilder: (BuildContext context, int index) {
if (index == state.currentSelectedAvatarIndex) {
return Center(
child: InkWell(
onTap: () => _onAvatarImageClicked(state.avatarUrlList[index], index),
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.red,
width: 4.0,
),
),
child: CircleAvatar(
radius: 60,
backgroundColor: Colors.transparent,
backgroundImage:
NetworkImage(state.avatarUrlList[index]),
),
),
),
);
} else {
return Center(
child: InkWell(
onTap: () => _onAvatarImageClicked(state.avatarUrlList[index], index),
child: CircleAvatar(
radius: 60,
backgroundColor: Colors.transparent,
backgroundImage: NetworkImage(state.avatarUrlList[index]),
),
),
);
}
},
),
);
} else if (state.isAvatarFetchInProgress) {
return LoadScreen();
} else {
return Text('Error');
}
},
);
}
When I want to do "Extract to widget", it raises an error : "reference to an enclosing class method cannot be extracted"
I know there is some variables that must get their data from class constructor but I want Android studio to extract the widget then, I will correct the mistaken codes, like Visual Studio that without any error extract the code to a new widget then it needs to copy the new extracted widget to a new dart file and correct the mistakes.
I want to extract the Card widget part.
import 'package:flutter/material.dart';
import 'package:flutter/material.dart' as prefix0;
import 'package:intl/intl.dart';
import '../model/transaction.dart';
class TransactionList extends StatelessWidget {
final List<Transaction> transactions;
final Function deleteTx;
TransactionList(this.transactions, this.deleteTx);
#override
Widget build(BuildContext context) {
return transactions.isEmpty
? LayoutBuilder(
builder: (ctx, constraint) {
return Column(
children: <Widget>[
Text(
'There is no transaction',
style: Theme.of(context).textTheme.title,
textDirection: prefix0.TextDirection.rtl,
),
SizedBox(
height: 10,
),
Container(
height: constraint.maxHeight * 0.6,
child: Image.asset(
'assets/images/yalda.png',
fit: BoxFit.cover,
))
],
);
},
)
: ListView.builder(
itemCount: transactions.length,
itemBuilder: (ctx, index) {
return **Card**(
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 5),
elevation: 5,
child: ListTile(
leading: CircleAvatar(
radius: 30,
child: Padding(
padding: const EdgeInsets.all(8),
child: FittedBox(
child: Text('\$${transactions[index].amount}')),
),
),
title: Text(
transactions[index].title,
style: Theme.of(context).textTheme.title,
),
subtitle: Text(DateFormat.yMMMd()
.format(transactions[index].date)
.toString()),
trailing: MediaQuery.of(context).size.width > 360
? FlatButton.icon(
onPressed: () => deleteTx(transactions[index].id),
icon: const Icon(Icons.delete),
label: const Text('Delete'),
textColor: Theme.of(context).errorColor,
)
: IconButton(
icon: const Icon(Icons.delete),
color: Theme.of(context).errorColor,
onPressed: () => deleteTx(transactions[index].id),
),
),
);
});
}
}
Simply use "Extract Method" instead of "Extract Widget". VSCode will add all the returns and references.
Edit: If you want to use "Extract Widget" only then simply wrap that widget in a Container and then use "Extract Widget" on that widget.
If that doesn't work, comment out setState() function inside the widget and try again.
Your deleteTx might contain a setState(() {}) method, try to comment that part of your code where you're calling deleteTx it and just put it back after your extraction.
Just remove or comment the setState() {} from your widget and it gonna works.
transform onpressed etc. property to comments and then try again 'Extract Widget' and go on
I had the same issue and in my case it was because of ListView.builder as yours.
So it is easy to fix, Simply make a class and return Card in Widget build and return it in ListView.builder inside the TransactionList class with the desired arguments.
You have to care about a few things:
Whenever you are extracting a widget, that widget should not contain any variable which is used in the current working page.
All the functions or methods should be parametric.
It is because you are referencing a variable(for example, transactions) in your Card widget from the enclosing class i.e. TransactionList. The best way to extract in this case could be to just make stateless/stateful widget outside your class and cut the Card widget and paste it as the return type of the build method of that Widget you created. And you can reference those variables using the constructor of that widget you created.
If you can comment out deleteTx(transactions[index].id) parts of your code and then use onPressed: (){}, you will be able to extract to widget.
After the extraction you can use:
onPressed: (){
setState(() {
deleteTx(transactions[index].id);
});
}
there might be some local referance to the variable in the current class,so if there some referance we cant extract a widget from there.
You can use the "Extract Method". It's a simple way. VSCode will add all the returns and references.
If we extract the widget it will StatelessWidget. StatelessWidget doesn't support changing state so if you use any Onchange property SetState it never extract
so please remove setState(() {});
Just remove or comment the setState() {} from your widget
Or
Just remove or comment the MediaQuery.of(context).size.width from your widget