Reference to an enclosing class method cannot be extracted on refactoring - android

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

Related

flutter Problem in vs code (Unable to Load asset)

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'));**

Flutter - How to handle Non-Nullable Instance of widget properties

I am new to flutter and currently trying to simple animation on my component, but i keep getting a " Non-Nullable Instance " error, i checked out other video tutorials but theirs worked perfectly while mine keeps throwing errors.
Here's my code.
class HomeScreen extends StatefulWidget {
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
Animation<Offset> sbarAnimation;
AnimationController sbarAnimationController;
#override
void initState() {
super.initState();
sbarAnimationController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 250),
);
sbarAnimation = Tween<Offset>(
begin: Offset(-1, 0),
end: Offset(0, 0),
).animate(
CurvedAnimation(
parent: sbarAnimationController,
curve: Curves.easeInOut,
),
);
}
And then the code for the widget is below
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: kBackgroundColor,
child: Stack(
children: [
SafeArea(
//so the background covers the entire app..
child: Column(
children: [
HomeScreenNavBar(),
ExploreCourseList(),
],
),
),
SlideTransition(
position: sbarAnimation, // this is where i call the _animation
child: SafeArea(
child: SidebarScreen(),
bottom: false,
),
),
],
),
),
);
}
}
below is the errors i keep getting, and most of the solutions i've seen did exactly the same thing, theirs works, but mine is giving this errors.. please what am i supposed to do.
There two way to solve the issue.
Which already answered by Juan Carlos Ramón Condezo, using late keyword, where you must have to initialize it before using anywhere.
You can make the variable nullable. By using '?' After declaring the variable type.
Animation?<Offset> sbarAnimation;
AnimationController? sbarAnimationController;
So, you can check if the variable is null and initialize it when needed.
Use late keyword to initialize variables later as you are doing in your initState
late Animation<Offset> sbarAnimation;
late AnimationController sbarAnimationController;

Correct way to manage state on dynamic widgets in Flutter

I have a list of categories in sqlite, I got those rows across streambuilder, then, I create a list of switch widgets. In each switch widget ontoggle event I change the source value and I call setState method, but, this causes the execution of the build event and reset all switch widgets values. After that I changed the code to store all widgets in a variable and in the streambuild if the widgets exists, return the widget list, this works almost well, this update the widgets values in sources, but, the widgets looks like if they were false.
Anyone has a clue?
Regards
StreamBuilder<List<Category>>( <--- this code is in build event
stream: _catBloc.categoriesStream,
builder: (BuildContext context, AsyncSnapshot<List<Category>> snapshot) {
if(!snapshot.hasData) {
return Container(
height: _size.height,
width: _size.width,
child: Center(
child: CircularProgressIndicator(),
),
);
} else if(_categoriesLinked.isEmpty){ <-- this is a map with the list of id and name
column = _createCategories(snapshot.data, widthSwitch);
return column;
} else {
return column;
}
},
),
Container( <-- this is inside _createCategories method
width: width,
child: Row(
children: [
FlutterSwitch(
width: _size.width * 0.06,
height: _size.height * 0.02,
toggleSize: 20.0,
value: _categoriesLinked[e.id],
showOnOff: false,
padding: 2.0,
activeColor: Color.fromRGBO(88, 203, 143, 0.25),
inactiveColor: Color.fromRGBO(224, 233, 240, 0.50),
activeToggleColor: Color.fromRGBO(88, 203, 143, 1.0),
inactiveToggleColor: Color.fromRGBO(78, 88, 96, 0.50),
onToggle: (val) => setState(() {
_categoriesLinked[e.id] = val;
}),
),
SizedBox(width: _size.width * 0.01,),
Flexible(child: Text("${ e.id}. ${ e.name}"))
]
),
)
In this case, you should refactor your code in order to create the switch as another widget, making them rebuilding only themselves.
For the state of the switch, you can simply get it from the parent using GlobalKey<MySwitchState> to access the children state during an onPressed method for example with: mySwitchKey.currentState
You should definitely avoid building stateful widget inside methods. If you feel the need of a method, create a new widget.

Force Flutter PopupMenuButton to repaint when locale (language is changed)

Does anybody know how do I get the list of CheckedPopupMenuItems to repaint (they obviously represent the list with options PopupMenuButton shows when pressed) when the list opened and visible at the very moment when I choose to change the locale/language of the android device?
For now when I do this everything on the screen gets repainted to reflect the change of the language, except the opened list. It gets repainted once I close it and open again.
Thanks for your suggestions!
You'll likely end up needing to use Keys. Basically your list (Even though information is being changed) is still technically the same widget as it was originally, and so your program doesn't rebuild the list when the information changes. If the rest of your app's widgets are being updated properly, that means you are setting the state properly, so adding the keys should be sufficient.
It'd be something like,
MapSample({Key key, this.title}) : super(key: key);
for more information on keys take a look at:
https://api.flutter.dev/flutter/widgets/Widget/key.html
Hope this helps!
Here's the code:
Widget _buildAndroid(BuildContext context) {
final width = MediaQuery.of(context).size.width;
return PopupMenuButton<ScreenOrientation>(
child: SizedBox(
width: width,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Icon(ScreenOrientation._getAndroidIconData(currentSelection), size: 30, color: iconColor),
SizedBox(width: 20),
Icon(Icons.expand_more, color: fontColor)
])),
padding: EdgeInsets.zero,
onSelected: (ScreenOrientation orientation) {
onScreenOrientationChanged(orientation);
},
itemBuilder: (BuildContext context) =>
ScreenOrientation.orientations
.map((o) => CheckedPopupMenuItem<ScreenOrientation>(
key: UniqueKey(),
selectedColor: selectedColor,
child: Row(
children: <Widget>[
Icon(ScreenOrientation._getAndroidIconData(o), size: 30, color: iconColor),
SizedBox(width: 10),
Text(L10n.of(context).tr((m) =>
m['settings']['orientations'][o._name]),
style: TextStyle(color: fontColor),
)
],
),
value: o,
checked: o == currentSelection,
))
.toList());
}
So the only interesting bit here is the L10n.of(context).tr((m) => m['settings']['orientations'][o._name] which essentially retrieves a localized string from the right json file whose name is determined based on the current locale.

How to implement a swipe to delete listview to remove data from firestore

Im very new to flutter and dart so this might be a basic question. However, what I would like to know is how to implement a swipe to delete method in a listview to delete data from firestore too.
I tried using the Dissmissible function but i dont understand how to display the list and I cant seem to understand how to remove the selected data as well.
This here is my dart code
Widget build(BuildContext context) {
return new Scaffold(
resizeToAvoidBottomPadding: false,
appBar: new AppBar(
centerTitle: true,
automaticallyImplyLeading: false,
title: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,children:
<Widget>[
Text("INVENTORY",textAlign: TextAlign.center,) ,new IconButton(
icon: Icon(
Icons.home,
color: Colors.black,
),
onPressed: () {
Navigator.push(
context,
SlideLeftRoute(widget: MyHomePage()),
);
})]),
),body: ListPage(),
);
}
}
class ListPage extends StatefulWidget {
#override
_ListPageState createState() => _ListPageState();
}
class _ListPageState extends State<ListPage> {
Future getPosts() async{
var firestore = Firestore.instance;
QuerySnapshot gn = await
firestore.collection("Inventory").orderBy("Name",descending:
false).getDocuments();
return gn.documents;
}
#override
Widget build(BuildContext context) {
return Container(
child: FutureBuilder(
future: getPosts(),
builder: (_, snapshot){
if(snapshot.connectionState == ConnectionState.waiting){
return Center(
child: Text("Loading"),
);
}else{
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder:(_, index){
return EachList(snapshot.data[index].data["Name"].toString(),
snapshot.data[index].data["Quantity"]);
});
}
}),
);
}
}
class EachList extends StatelessWidget{
final String details;
final String name;
EachList(this.name, this.details);
#override
Widget build(BuildContext context) {
// TODO: implement build
return new Card(
child:new Container(
padding: EdgeInsets.all(8.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Row(
children: <Widget>[
new CircleAvatar(child: new Text(name[0].toUpperCase()),),
new Padding(padding: EdgeInsets.all(10.0)),
new Text(name, style: TextStyle(fontSize: 20.0),),
],
),
new Text(details, style: TextStyle(fontSize: 20.0))
],
),
),
);
}
}
You should use Dismissible widget. I used it for an inbox list retrieved from Firestore. Inside your EachList return something like this
return Dismissible(
direction: DismissDirection.startToEnd,
resizeDuration: Duration(milliseconds: 200),
key: ObjectKey(snapshot.documents.elementAt(index)),
onDismissed: (direction) {
// TODO: implement your delete function and check direction if needed
_deleteMessage(index);
},
background: Container(
padding: EdgeInsets.only(left: 28.0),
alignment: AlignmentDirectional.centerStart,
color: Colors.red,
child: Icon(Icons.delete_forever, color: Colors.white,),
),
// secondaryBackground: ...,
child: ...,
);
});
IMPORTANT: in order to remove the list item you'll need to remove the item from the snapshot list as well, not only from firestore:
_deleteMessage(index){
// TODO: here remove from Firestore, then update your local snapshot list
setState(() {
snapshot.documents.removeAt(index);
});
}
Here the doc: Implement Swipe to Dismiss
And here a video by Flutter team: Widget of the week - Dismissilbe
You can use the flutter_slidable package to achieve the same.
You can also check out my Cricket Team on Github in which I have did the same you want to achieve, using same package.
Example for how to use package are written here.
I'd like to add that when deleting a document from Firestore, no await is needed as the plugin automatically caches the changes and then syncs them up when there is a connection again.
For instance, I used to use this method
Future deleteWatchlistDocument(NotifierModel notifier) async {
final String uid = await _grabUID();
final String notifierID = notifier.documentID;
return await _returnState(users.document(uid).collection(watchlist).document(notifierID).delete());
}
in which I was waiting for the call to go through, however this prevented any other call to go through and only allowed one. Removing this await tag however solved my issue.
Now I can delete documents offline, and the changes will sync up with Firestore when a connection is regained. It's pretty cool to watch in the console.
I'd recommend watching this video about offline use with Firestore

Categories

Resources