I was trying to use TweenAnimationBuilder<bool> but I really don't know what was the problem even I had the exception logs. I tired googling before posting this question, but it was of no help.
Exception:
Class 'bool' has no instance method '-'.
Receiver: true
Tried calling: -(false)
The relevant error-causing widget was TweenAnimationBuilder<bool>
Code:
#override
Widget build(BuildContext context) {
return TweenAnimationBuilder<bool>(
duration: Duration(seconds: 2),
tween: Tween<bool>(begin: false, end: true),
builder: (BuildContext context, bool tween, Widget child) {
print(tween.toString());
return Visibility(visible: showNavBar, child: child);
},
child: BottomNavigationBar(
selectedItemColor: Colors.green,
unselectedItemColor: Colors.black,
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: "Home"),
BottomNavigationBarItem(icon: Icon(Icons.explore), label: "Home"),
BottomNavigationBarItem(
icon: Icon(Icons.library_books), label: "Home"),
BottomNavigationBarItem(icon: Icon(Icons.settings), label: "Home")
],
onTap: (index) {},
),
);
}
TweenAnimationBuilder<bool> attempts to use a Tween<bool>, but Tween<T> cannot be used with bool. From the Tween documentation:
Types with special considerations
...
Types that define + and - operators to combine values (T + T → T and T - T → T) and an * operator to scale by multiplying with a double (T * double → T) can be directly used with Tween<T>.
bool does not provide +, -, and * operators.
Related
Helllo
I have a Product Category screen on my Flutter App. I have added a IconButton for animation for 'Add To Cart' action. But if you click add to cart icon on product image, all icons are animating. I want to animate only one icon. Can you help me?
IconButton(
icon: AnimatedSwitcher(
duration: const Duration(milliseconds: 350),
transitionBuilder: (child, anim) => RotationTransition(
turns: child.key == ValueKey('icon1')
? Tween<double>(begin: 1, end: 0.75).animate(anim)
: Tween<double>(begin: 0.75, end: 1).animate(anim),
child: ScaleTransition(scale: anim, child: child),
),
child: _currIndex == 0
? Icon(Icons.close, key: const ValueKey('icon1'))
: Icon(
Icons.arrow_back,
key: const ValueKey('icon2'),
)),
onPressed: () {
setState(() {
_currIndex = _currIndex == 0 ? 1 : 0;
});
},
),
I think the issue is because you are using _currIndex in each IconButton, so when you change one you make setState to change the value then every product gets affected by this change.
what you have to do is separate each product from the other...
one way is to make _currIndex a local variable if you use ListView put the variable in its scope but I doubt this works
the other way is to make a list of Booleans of size equal to the number of products and you can check if the _currIndex of a product is 0 or 1 by accessing the List with the index of the ListView.
so it will look like this:
// at the beginning of the file you will have a list of booleans with all values set to zero by doing this
List<bool> isInCart = List.filled(numberOfProducts, false);
// then you will do your code but with the list not the _currIndex
IconButton(
icon: AnimatedSwitcher(
duration: const Duration(milliseconds: 350),
transitionBuilder: (child, anim) => RotationTransition(
turns: child.key == ValueKey('icon1')
? Tween<double>(begin: 1, end: 0.75).animate(anim)
: Tween<double>(begin: 0.75, end: 1).animate(anim),
child: ScaleTransition(scale: anim, child: child),
),
// if the element is true then this product is in the cart otherwise its not
child: isInCart[index]
? Icon(Icons.close, key: const ValueKey('icon1'))
: Icon(
Icons.arrow_back,
key: const ValueKey('icon2'),
)),
onPressed: () {
setState(() {
// you just invert the value here
isInCart[index] = !isInCart[index];
});
},
),
I'm using index stack in home screen to show different screen at one time.
My Problem is that child widget is rebuilt again and make API call when ever I re-enter to any screen
home screen code:
final _currentPage =
context.select<MenuProvider, int>((provider) => provider.currentPage);
void _onItemTapped(int index) {
Provider.of<MenuProvider>(context, listen: false)
.updateCurrentPage(index);
}
List<MenuItem> mainMenu = [
MenuItem(
AppLocalizations.of(context)!.sd_title,
'Home',
Icons.home,
0,
),
MenuItem(
AppLocalizations.of(context)!.profile_title,
'Profile',
Icons.person,
1,
),
MenuItem(
AppLocalizations.of(context)!.sd_calculator,
'Calculator',
Icons.calculate_rounded,
2,
),
];
var screens = [
mainMenu[_currentPage].index == 0 ? const HomeFragment() : Container(),
mainMenu[_currentPage].index == 1 ? const Profile() : Container(),
mainMenu[_currentPage].index == 2
? LoanCalculatorScreen(isHome: true)
: Container(),
];
var container = Container(
alignment: Alignment.center,
color: Colors.white,
child: FadeIndexedStack(
index: mainMenu[_currentPage].index,
children: screens,
),
);
Container is used in body of Scaffold.
Bottom Navigation:
bottomNavigationBar: BottomNavigationBar(
items: mainMenu
.map(
(item) => BottomNavigationBarItem(
label: item.bottomTitle,
icon: Icon(
item.icon,
),
),
)
.toList(),
currentIndex: _currentPage,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
)
Home screen: As statefull to make a call on init state;
#override
void initState() {
var homeData = Provider.of<HomeProvider>(context, listen: false);
homeData.getOffersAndPartnersSlider();
super.initState();
}
I'm using Provider for state management and API call.
any suggestion will help me to write better code and make good performance.
I've Try Page View also same thing happen the inside child is rebuilt.
I just want to make The API call once.
To fix this I've made changes with Pages.
Declare list of pages above built method with PageStorageKey.
final List<Widget> pages = [
const HomeFragment(
key: PageStorageKey('Page1'),
),
const Profile(
key: PageStorageKey('Page2'),
),
Lo
LoanCalculatorScreen(key: const PageStorageKey('Page'), isHome: true),
];
And Call pages inside Indexed Stack Widgets Children
Container(
alignment: Alignment.center,
color: Colors.white,
child: FadeIndexedStack(
index: mainMenu[_currentPage].index,
children: pages,
),
)
I am using a ListView.Builder to display my data to the screen which is working very fine, then I implemented the Dismiss and onDismiss function which seem to be working fine but it I try to dismiss item-A it will dismiss it from the screen but on the showSnackBar its displaying that Item-Z was deleted, if I deleted item-B it still gives me the name of of item-Z on the showSnackBar, I added more items to the data List which made item-Z now item-X but if I swipe its still shows me that the new item-Z has been deleted, then I tried deleting the Last Item which is the item-Z and it gave me an error
[. Exception caught by animation library
═════════════════════════════════ The following RangeError was thrown
while notifying listeners for AnimationController:
RangeError (index): Invalid value: Not in inclusive range 0..3: 4
When the exception was thrown, this was the stack
#0 List.[] (dart:core-patch/growable_array.dart:177:60)
#1 _ListsState.build.. package:another_test/list_widget.dart:81. ]
this is the code down bellow
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import './list_model.dart';
class Lists extends StatefulWidget {
#override
_ListsState createState() => _ListsState();
}
class _ListsState extends State<Lists> {
List<ItemLists> items = [
ItemLists(
title: 'Best Music of the Year',
discription: 'Davido',
favorite: false,
),
ItemLists(
title: 'Best Album Cover design',
discription: 'Brighter Press',
favorite: false,
),
ItemLists(
title: 'Best Vocalist',
discription: 'Simi-Sola',
favorite: false,
),
ItemLists(
title: 'Best Danced',
discription: 'Black Camaru',
favorite: false,
),
ItemLists(
title: 'Best Performance',
discription: 'Shofeni-Were',
favorite: false,
),
ItemLists(
title: 'Best Act',
discription: 'You Want to See Craze',
favorite: false,
),
];
#override
Widget build(BuildContext context) {
return Container(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (context, index) {
return Dismissible(
key: ObjectKey(items[index]),
background: Container(
color: Colors.red,
),
child: Card(
child: ListTile(
leading: new IconButton(
icon: Icon(
Icons.star,
color: items[index].favorite ? Colors.green : Colors.grey,
),
tooltip: 'Add to Favorite',
onPressed: () {
setState(() {
items[index].favorite = !items[index].favorite;
});
}),
title: Text('${items[index].title}'),
subtitle: Text('${items[index].discription}'),
trailing:
IconButton(icon: Icon(Icons.calendar_today), onPressed: null),
)),
onDismissed: (direction) {
// Remove the item from the data source.
setState(() {
items.removeAt(index);
});
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('${items[index]?.title} Deleted'),
),
);
},
);
},
itemCount: items.length,
),
);
}
}
and the data model code down bellow
class ItemLists {
String title;
String discription;
bool favorite;
ItemLists({this.title, this.discription, this.favorite});
}
from the screenshoot bellow you will see i deleted a different item but it was still showing me that i deleted the last item which is still on the page
Snackbar is trying to get the name, but the item is already deleted by that time. The solution would be to get the title name before the item is deleted from the list.
Change onDismissed to this:
onDismissed: (direction) {
// Remove the item from the data source.
// get the title of the item before it is deleted
final String myTitle = items[index].title;
setState(() {
items.removeAt(index);
});
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('$myTitle Deleted'),
),
);
};
I want to navigate to QrScan screen once the icons get pressed, instead, I got an error!!
setState() or markNeedsBuild() called during build
I want to navigate to that screen and get data from QR Codes, after that I want this data to be shown on another screen!
It says:
This Overlay widget cannot be marked as needing to build because the framework is already in the process of building widgets.
A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building.
This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built.
Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was:
Overlay- [LabeledGlobalKey#a5a46]
The widget which was currently being built when the offending call wasmade was: builder
class MainTabsScreen extends StatefulWidget {
#override
_MainTabsScreenState createState() => _MainTabsScreenState();
}
class _MainTabsScreenState extends State<MainTabsScreen> {
int page = 3;
void _openScanner() {
Navigator.push(context, MaterialPageRoute(builder: (context) => QrScan()));
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Builder(
builder: (context) {
switch (page) {
case 0:
return ExploreScreen();
case 1:
return OffersScreen();
case 2:
_openScanner();
break;
case 3:
return AltersScreen();
case 4:
return ChatsScreen();
default:
return ExploreScreen();
}
},
),
),
bottomNavigationBar: ConvexAppBar(
top: -20.0,
backgroundColor: Colors.white,
activeColor: Color(0xBB0BCC83),
color: Color(0xBB0BCC83),
height: 53.0,
elevation: 0.0,
initialActiveIndex: 3,
items: [
TabItem(
icon: Icons.home,
title: 'Home',
),
TabItem(
icon: Icons.list,
title: 'Offers',
),
TabItem(
icon: Icons.qr_code,
title: 'Scan',
),
TabItem(
icon: Icons.add_alert,
title: 'Notification',
),
TabItem(
icon: Icons.chat,
title: 'Chats',
),
],
onTap: (id) {
setState(() => page = id);
},
),
);
}
}
As discussed in comments, a solution was to call the navigator.push when id == 2 within the onTap function.
I'm working on a Flutter app and trying to make my bottom navigator bar more universal. As part of that, I've moved it to its own file and now need to simply lift state up to the parent class in order to highlight the selected icon. I call it like so from the parent:
bottomNavigationBar: buildBottomNavBar(
selectedIndex: selectedIndex,
redrawDisplay: redrawDisplay,
context: context,
),
And the bottomNaviBar is built like so:
return BottomNavigationBar(
currentIndex: selectedIndex,
onTap: (int index) {
selectedIndex = index;
redrawDisplay(selectedIndex);
onBottomMenuTap(index, appProject.picsSelectable, context);
},
elevation: 5.0,
iconSize: 30.0,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.note_add),
label: 'Add Metadata',
),
BottomNavigationBarItem(
icon: Icon(Icons.share),
label: 'Share Pics',
),
BottomNavigationBarItem(
icon: Icon(Icons.delete),
label: 'Delete Pics',
),
],
);
The 'redrawDisplay' function is simply a function in the parent class that sets state, like so:
void redrawDisplay({int selectedIndex}) {
setState(() {
this.selectedIndex = selectedIndex ?? 0;
});
}
I swear this was working a few days ago, but now I'm getting the following exception:
════════ Exception caught by gesture ═════════════════════════════════════════════
Closure call with mismatched arguments: function '_SearchScreenState.redrawDisplay'
Receiver: Closure: ({int selectedIndex}) => dynamic from Function 'redrawDisplay':.
Tried calling: _SearchScreenState.redrawDisplay(0)
Found: _SearchScreenState.redrawDisplay({int selectedIndex}) => dynamic
═══════════════════════════════════════════════════════════════════════
Obviously something has changed in my code, but I have been staring at this for hours and haven't cracked it yet. Anybody have some insight into what I've screwed up?
Thanks in advance.