flutter web inkwell onhover causing exception - flutter

When I hover the icon button and then hover to another position always in the InkWell region, I get this exception:
Error: Assertion failed:
../…/animation/animation_controller.dart:487
_ticker != null
"AnimationController.reverse() called after AnimationController.dispose()\nAnimationController methods should not be used after calling dispose."
at Object.throw_ [as throw] (http://localhost:38805/dart_sdk.js:5063:11)
at Object.assertFailed (http://localhost:38805/dart_sdk.js:4988:15)
at animation_controller.AnimationController.new.reverse (http://localhost:38805/packages/flutter/src/animation/animation_controller.dart.lib.js:305:42)
at internalCallback (http://localhost:38805/dart_sdk.js:26215:11)
This is the code:
return Material(
child: InkWell(
onTap: widget.onTap,
onHover: (bool isHoverIn) {
print("isHoverIN: $isHoverIn iscancel: $isCancelButtonVisible");
// (isHoverIn) {
if (isHoverIn != isCancelButtonVisible)
setState(() {
isCancelButtonVisible = isHoverIn;
});
},
child: ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: Container(
height: boxSize,
width: boxSize,
decoration: BoxDecoration(
image: DecorationImage(
image: widget.imageData.image!.image,
fit: BoxFit.cover,
),
border: Border.all(
color: Colors.grey,
width: 5,
),
borderRadius: BorderRadius.circular(12),
),
child: isCancelButtonVisible
? Align(
alignment: Alignment.topRight,
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.cancel),
tooltip: 'rimuovi',
onPressed: () => widget.onRemove(widget.position),
),
)
: null,
)),
),
);
Printing the hover actions from the IconButton and out the IconButton in the InkWell region:
isHoverIN: true iscancel: false
isHoverIN: false iscancel: true
isHoverIN: true iscancel: false
This is the full widget:
class _ProductMediaViewer extends StatefulWidget {
final int position;
final ProductVariantImage imageData;
final void Function(int, int) swap;
final void Function(int) onRemove;
final void Function() onTap;
final double boxSize;
const _ProductMediaViewer({
Key? key,
required this.position,
required this.imageData,
required this.swap,
required this.onRemove,
required this.onTap,
required this.boxSize,
}) : super(key: key);
#override
_ProductMediaViewerState createState() => _ProductMediaViewerState();
}
class _ProductMediaViewerState extends State<_ProductMediaViewer> {
bool isCancelButtonVisible = false;
Widget getMediaContentBox(bool isTransparent) {
final boxSize =
widget.position == -1 ? widget.boxSize * 2.5 : widget.boxSize;
return Material(
color: Colors.transparent,
child: Opacity(
opacity: isTransparent ? 0.45 : 1.0,
child: InkWell(
// TODO
onTap: widget.onTap,
onHover: (bool isHoverIn) {
print("isHoverIN: $isHoverIn iscancel: $isCancelButtonVisible");
// (isHoverIn) {
if (isHoverIn != isCancelButtonVisible)
setState(() {
isCancelButtonVisible = isHoverIn;
});
},
child: ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: Container(
height: boxSize,
width: boxSize,
decoration: BoxDecoration(
image: DecorationImage(
image: widget.imageData.image!.image,
fit: BoxFit.cover,
),
border: Border.all(
color: Colors.grey,
width: 5,
),
borderRadius: BorderRadius.circular(12),
),
child: isCancelButtonVisible
? Align(
alignment: Alignment.topRight,
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.cancel),
tooltip: 'rimuovi',
onPressed: () => widget.onRemove(widget.position),
),
)
: null,
)),
),
),
);
}
#override
Widget build(BuildContext context) {
final boxSize =
widget.position == -1 ? widget.boxSize * 2.5 : widget.boxSize;
return Draggable<int>(
onDragCompleted: () => isCancelButtonVisible = false,
maxSimultaneousDrags: 1,
data: widget.position,
child: DragTarget<int>(
builder: (
BuildContext context,
List<dynamic> accepted,
List<dynamic> rejected,
) {
return getMediaContentBox(false);
},
onAccept: (int draggablePosition) {
widget.swap(draggablePosition, widget.position);
print("draggpos: $draggablePosition");
print("pos: ${widget.position}");
},
),
feedback: getMediaContentBox(true),
childWhenDragging: Container(
height: boxSize,
width: boxSize,
color: Colors.grey[200],
),
);
}
}

You can use this widget
class HoverWidget extends StatefulWidget {
final Widget child;
final Widget hoverChild;
final Function(PointerEnterEvent event) onHover;
const HoverWidget(
{Key key,
#required this.child,
#required this.hoverChild,
#required this.onHover})
: assert(child != null && hoverChild != null && onHover != null),
super(key: key);
#override
_HoverWidgetState createState() => _HoverWidgetState();
}
class _HoverWidgetState extends State<HoverWidget> {
bool _isHover = false;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: (event) {
setState(() {
_isHover = true;
});
widget.onHover(event);
},
onExit: (event) {
setState(() {
_isHover = false;
});
},
child: _isHover ? widget.hoverChild : widget.child,
);
}
}

Related

why is (_selected) in Widged _button undfined

in the Widget _button it says:
Undefined name '_selected'.
Try correcting the name to one that is defined, or defining the name.dartundefined_identifier
But I defined at in Widget section please help
or say me what do i wrong
it is the same with _setState
class NavBar extends StatefulWidget {
const NavBar({Key? key}) : super(key: key);
#override
_NavBarState createState() => _NavBarState();
}
class _NavBarState extends State<NavBar> {
final _palette = AppTheme.palette;
int _selected = 0;
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: _palette.primaryColor,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_button(
index: 0,
icon: Icons.home,
selectedIndex: _selected,
),
_button(
index: 1,
icon: Icons.favorite_border_outlined,
selectedIndex: _selected,
),
],
),
);
}
}
on the same page is the Widget _button
Widget _button({
required int index,
required IconData icon,
VoidCallback? onPressed,
int selectedIndex: 0,
}) {
bool isSelected = selectedIndex == index;
return Material(
color: isSelected ? AppTheme.palette.buttonOverlay : Colors.transparent,
borderRadius: BorderRadius.circular(13),
clipBehavior: Clip.antiAlias,
child: IconButton(
visualDensity: VisualDensity.compact,
icon: Icon(
icon,
color: isSelected
? AppTheme.palette.secondaryColor
: AppTheme.palette.buttonOverlay,
),
onPressed: () {
_selected = index;
onPressed?.call();
setState(() {});
},
),
);
}
...on the same page is the Widget _button
You need to make sure that _button is within your NavBar class. It should look like this:
class NavBar extends StatefulWidget {
const NavBar({Key? key}) : super(key: key);
#override
_NavBarState createState() => _NavBarState();
}
class _NavBarState extends State<NavBar> {
final _palette = AppTheme.palette;
int _selected = 0;
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: _palette.primaryColor,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_button(
index: 0,
icon: Icons.home,
selectedIndex: _selected,
),
_button(
index: 1,
icon: Icons.favorite_border_outlined,
selectedIndex: _selected,
),
],
),
);
}
Widget _button({
required int index,
required IconData icon,
VoidCallback? onPressed,
int selectedIndex: 0,
}) {
bool isSelected = selectedIndex == index;
return Material(
color: isSelected ? AppTheme.palette.buttonOverlay : Colors.transparent,
borderRadius: BorderRadius.circular(13),
clipBehavior: Clip.antiAlias,
child: IconButton(
visualDensity: VisualDensity.compact,
icon: Icon(
icon,
color: isSelected
? AppTheme.palette.secondaryColor
: AppTheme.palette.buttonOverlay,
),
onPressed: () {
_selected = index;
onPressed?.call();
setState(() {});
},
),
);
}
}
Make sure your _button method is inside the _NavBarState class, otherwise you can't access the global data inside it.

Translate chat message based on language that user picks from drop down list

I am trying to get messages translated in real-time in the chat portion of my app depending on the language that the user picks in real-time. For example, if the user only speaks Spanish but messages from the user that they are chatting with are in English, then the user can select 'Spanish' from the dropdown list and all messages that have already been received and all future messages that they will receive will be translated into Spanish. I am capturing the sent message and its translation in each language in firebase but not sure how to get the messages to actually translate on the frontend. Any help would be much appreciated. Thank you in advance!
chat.dart
class Chat extends StatelessWidget {
final String? peerId;
final String? peerAvatar;
final String? name;
Chat({Key? key, this.peerId, this.peerAvatar, this.name}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: SkapeColors.pageBackgroundFifth,
resizeToAvoidBottomInset: true,
appBar: AppBar(
iconTheme: IconThemeData(
color: Colors.white,
),
backgroundColor: SkapeColors.pageBackground,
title: Text(
name!,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: 'Brand-Bold', fontWeight: FontWeight.bold, color: Colors.white, fontSize: 25),
),
centerTitle: true,
),
body: ChatScreen(
peerId: peerId,
peerAvatar: peerAvatar,
name: name,
),
);
}
}
class ChatScreen extends StatefulWidget {
final String? peerId;
final String? peerAvatar;
final String? name;
ChatScreen({Key? key, this.peerId, this.peerAvatar, this.name})
: super(key: key);
#override
State createState() =>
ChatScreenState(peerId: peerId, peerAvatar: peerAvatar);
}
class ChatScreenState extends State<ChatScreen> {
ChatScreenState({Key? key, this.peerId, this.peerAvatar, this.name});
String? peerId;
String? peerAvatar;
String? name;
String? id;
String? language1 = Translations.languages.first;
String? language2 = Translations.languages.first;
final translator = GoogleTranslator();
static final _apiKey = 'hidden';
List<QueryDocumentSnapshot> listMessage = new List.from([]);
int _limit = 20;
int _limitIncrement = 20;
String groupChatId = "";
SharedPreferences? prefs;
File? imageFile;
bool isLoading = false;
bool isShowSticker = false;
String imageUrl = "";
final TextEditingController textEditingController = TextEditingController();
final ScrollController listScrollController = ScrollController();
final FocusNode focusNode = FocusNode();
_scrollListener() {
if (listScrollController.offset >=
listScrollController.position.maxScrollExtent &&
!listScrollController.position.outOfRange) {
setState(() {
_limit += _limitIncrement;
});
}
}
#override
void initState() {
super.initState();
focusNode.addListener(onFocusChange);
listScrollController.addListener(_scrollListener);
readLocal();
}
readLocal() async {
prefs = await SharedPreferences.getInstance();
id = await getUserID();
if (id.hashCode <= peerId.hashCode) {
groupChatId = '$id-$peerId';
} else {
groupChatId = '$peerId-$id';
}
FirebaseFirestore.instance
.collection('users')
.doc(id)
.update({'chattingWith': peerId});
setState(() {});
}
static Future<String> translate(String message, String toLanguageCode) async {
final response = await http.post(
Uri.parse('https://translation.googleapis.com/language/translate/v2?target=$toLanguageCode&key=$_apiKey&q=$message'),
);
if (response.statusCode == 200) {
final body = json.decode(response.body);
final translations = body['data']['translations'] as List;
final translation = translations.first;
return HtmlUnescape().convert(translation['translatedText']);
} else {
throw Exception();
}
}
static Future<String> translate2(
String message, String fromLanguageCode, String toLanguageCode) async {
final translation = await GoogleTranslator().translate(
message,
from: fromLanguageCode,
to: toLanguageCode,
);
return translation.text;
}
Future<void> onSendMessage(String content, int type) async {
// type: 0 = text, 1 = image, 2 = sticker
if (content.trim() != '') {
textEditingController.clear();
var documentReference = FirebaseFirestore.instance
.collection('messages')
.doc(groupChatId)
.collection(groupChatId)
.doc(DateTime.now().millisecondsSinceEpoch.toString());
FirebaseFirestore.instance.runTransaction((transaction) async {
transaction.set(
documentReference,
{
'idFrom': id,
'idTo': peerId,
'timestamp': DateTime.now().millisecondsSinceEpoch.toString(),
'content': content,
'translated': {
'english': await translate(content, 'en'),
'spanish': await translate(content, 'es'),
'german': await translate(content, 'de'),
'french': await translate(content, 'fr'),
'russian': await translate(content, 'ru'),
'italian': await translate(content, 'it'),
'selectedTranslation': language1,
},
'type': type
},
);
});
listScrollController.animateTo(0.0,
duration: Duration(milliseconds: 300), curve: Curves.easeOut);
try {
String body = content;
if (content.contains("firebasestorage")) {
body = "Image";
}
var tempResp = await getUserInformation();
await sendNotificationToUser(
peerId, "New message from " + tempResp["fullName"], body);
} catch (e) {
print(e);
}
} else {
Fluttertoast.showToast(
msg: 'Nothing to send. Please insert your message',
backgroundColor: Colors.white24,
textColor: SkapeColors.colorPrimary);
}
}
Widget buildItem(int index, DocumentSnapshot document) {
String language1 = Translations.languages.first;
String language2 = Translations.languages.first;
if (document != null) {
if (document.get('idFrom') == id) {
// Right (my message)
return Row(
children: <Widget>[
document.get('type') == 0
// Text
? Container(
child: TranslationWidget(
message: document.get('content'),
fromLanguage: language1,
toLanguage: language1,
builder: (translatedMessage)=> MessageWidget(message: document.get('content'), translatedMessage: document.get('content'), isMe: true),
),
padding: EdgeInsets.fromLTRB(5.0, 5.0, 5.0, 5.0),
width: 215.0,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondary,
borderRadius: BorderRadius.circular(8.0)),
margin: EdgeInsets.only(
bottom: isLastMessageRight(index) ? 20.0 : 10.0,
right: 10.0),
)
: document.get('type') == 1
// Image
? Container(
child: OutlinedButton(
child: Material(
child: Image.network(
document.get("content"),
loadingBuilder: (BuildContext context,
Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Container(
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.all(
Radius.circular(8.0),
),
),
width: 200.0,
height: 200.0,
child: Center(
child: CircularProgressIndicator(
color: SkapeColors.colorPrimary,
value: loadingProgress
.expectedTotalBytes !=
null &&
loadingProgress
.expectedTotalBytes !=
null
? loadingProgress
.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
),
);
},
width: 200.0,
height: 200.0,
fit: BoxFit.cover,
),
borderRadius:
BorderRadius.all(Radius.circular(8.0)),
clipBehavior: Clip.hardEdge,
),
onPressed: () {
print("here");
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FullPhoto(
url: document.get('content'),
),
),
);
},
// Sticker
: Container(
child: Image.asset(
'images/${document.get('content')}.gif',
width: 100.0,
height: 100.0,
fit: BoxFit.cover,
),
margin: EdgeInsets.only(
bottom: isLastMessageRight(index) ? 20.0 : 10.0,
right: 10.0),
),
],
mainAxisAlignment: MainAxisAlignment.end,
);
} else {
// Left (peer message)
return Container(
child: Column(
children: <Widget>[
Row(
children: <Widget>[
isLastMessageLeft(index)
? Material(
child: Image.network(
peerAvatar!,
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
color: SkapeColors.colorPrimary,
value: loadingProgress.expectedTotalBytes !=
null &&
loadingProgress.expectedTotalBytes !=
null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
);
},
errorBuilder: (context, object, stackTrace) {
return Image.asset(
'images/user_icon.png',
height: 50,
width: 50,
);
},
width: 40,
height: 40,
fit: BoxFit.cover,
),
borderRadius: BorderRadius.all(
Radius.circular(35.0),
),
clipBehavior: Clip.hardEdge,
)
: Container(width: 35.0),
document.get('type') == 0
? Container(
child:
TranslationWidget(
message: document.get('content'),
fromLanguage: language1,
toLanguage: language1,
builder: (translatedMessage)=> MessageWidget(message: document.get('content'), translatedMessage: document.get('content'), isMe: false),
),
padding: EdgeInsets.fromLTRB(10.0, 5.0, 5.0, 5.0),
width: 215.0,
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(8.0)),
margin: EdgeInsets.only(
bottom: isLastMessageRight(index) ? 20.0 : 10.0,
right: 10.0),
).paddingOnly(left: 12)
: document.get('type') == 1
? Container(
child: TextButton(
child: Material(
child: Image.network(
document.get('content'),
loadingBuilder: (BuildContext context,
Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Container(
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.all(
Radius.circular(8.0),
),
),
width: 200.0,
height: 200.0,
child: Center(
child: CircularProgressIndicator(
color: SkapeColors.colorPrimary,
value: loadingProgress
.expectedTotalBytes !=
null &&
loadingProgress
.expectedTotalBytes !=
null
? loadingProgress
.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
),
);
},
width: 200.0,
height: 200.0,
fit: BoxFit.cover,
),
borderRadius:
BorderRadius.all(Radius.circular(8.0)),
clipBehavior: Clip.hardEdge,
),
onPressed: () {
},
)
: Container(
),
],
),
)
],
crossAxisAlignment: CrossAxisAlignment.start,
),
margin: EdgeInsets.only(bottom: 2.0),
);
}
} else {
return SizedBox.shrink();
}
}
bool isLastMessageLeft(int index) {
if ((index > 0 && listMessage[index - 1].get('idFrom') == id) ||
index == 0) {
return true;
} else {
return false;
}
}
bool isLastMessageRight(int index) {
if ((index > 0 && listMessage[index - 1].get('idFrom') != id) ||
index == 0) {
return true;
} else {
return false;
}
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: ()=> Future.value(true),
child: Stack(
children: <Widget>[
Column(
children: <Widget>[
// List of messages
buildListMessage(),
// Input content
buildInput(),
],
),
// Loading
buildLoading()
],
),
// onWillPop: onBackPress,
);
}
Widget buildLoading() {
return Positioned(
child: isLoading ? const Loading() : Container(),
);
}
Widget buildInput() {
return Container(
child: SingleChildScrollView(
child: Column(
children: [
buildTitle().paddingBottom(10),
Row(
),
SizedBox(width: 10,),
Flexible(
child: Container(
child: TextField(
cursorColor: SkapeColors.colorPrimary,
autocorrect: true,
onSubmitted: (value) {
onSendMessage(textEditingController.text, 0);
},
style: TextStyle(color: Colors.white, fontSize: 18.0),
controller: textEditingController,
decoration: InputDecoration.collapsed(
hintText: 'Send Message....',
hintStyle: TextStyle(color: SkapeColors.colorTextSemiLight),
),
focusNode: focusNode,
),
),
),
// Button send message
color: SkapeColors.pageBackground,
),
],
),
],
),
),
width: double.infinity,
height: 155.0,
// height: 100,
);
}
Widget buildListMessage() {
return Flexible(
child: groupChatId.isNotEmpty
? StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('messages')
.doc(groupChatId)
.collection(groupChatId)
.orderBy('timestamp', descending: true)
.limit(_limit)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
listMessage.addAll(snapshot.data!.docs);
return ListView.builder(
padding: EdgeInsets.all(10.0),
itemBuilder: (context, index) => buildItem(index, snapshot.data!.docs[index]),
itemCount: snapshot.data?.docs.length,
reverse: true,
controller: listScrollController,
);
} else {
return Center(
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color>(SkapeColors.colorPrimary),
),
);
}
},
)
: Center(
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color>(SkapeColors.colorPrimary),
),
),
);
}
Widget buildTitle() => TitleWidget(
language1: language1,
onChangedLanguage1: (newLanguage) => setState(() {
language1 = newLanguage;
}), key: ValueKey(DropDownWidget),
);
}
TitleWidget.dart
import 'package:flutter/material.dart';
import '../screens/messaging/chatWidgets/DropDownWidget.dart';
class TitleWidget extends StatelessWidget {
final String? language1;
final ValueChanged<String?> onChangedLanguage1;
const TitleWidget({
required this.language1,
required this.onChangedLanguage1,
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) => Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.translate, color: Colors.grey, size: 30,),
SizedBox(width: 12,),
DropDownWidget(
value: language1??"",
onChangedLanguage: onChangedLanguage1, key: key!,
),
],
);
}
translations.dart
class Translations {
static final languages = <String>[
'English',
'Spanish',
'French',
'German',
'Italian',
'Russian'
];
static String getLanguageCode(String language) {
switch (language) {
case 'English':
return 'en';
case 'French':
return 'fr';
case 'Italian':
return 'it';
case 'Russian':
return 'ru';
case 'Spanish':
return 'es';
case 'German':
return 'de';
default:
return 'en';
}
}
}
MessageWidget.dart
import 'package:flutter/material.dart';
class MessageWidget extends StatelessWidget {
final String? message;
final String? translatedMessage;
final bool isMe;
const MessageWidget({
required this.message,
required this.translatedMessage,
required this.isMe,
});
#override
Widget build(BuildContext context) {
final radius = Radius.circular(4);
final borderRadius = BorderRadius.all(radius);
return Row(
//To align at different positions based on if message is from the user or not
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
// padding: EdgeInsets.only(right: 10),
// margin: EdgeInsets.only(right: 10),
constraints: BoxConstraints(maxWidth: 190),
decoration: BoxDecoration(
color: isMe ? Theme.of(context).colorScheme.secondary : Colors.grey,
borderRadius: isMe
? borderRadius.subtract(BorderRadius.only(bottomRight: radius))
: borderRadius.subtract(BorderRadius.only(bottomLeft: radius)),
),
child: buildMessage(),
),
],
);
}
Widget buildMessage() => Column(
crossAxisAlignment:
isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: <Widget>[
// Text(
// message,
// style: TextStyle(
// color: isMe ? Colors.black54 : Colors.white70,
// fontSize: 14,
// ),
// textAlign: isMe ? TextAlign.end : TextAlign.start,
// ),
Text(
translatedMessage!,
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.start,
),
],
);
}
TranslationWidget.dart
class TranslationWidget extends StatefulWidget {
final String? message;
final String? fromLanguage;
final String? toLanguage;
final Widget Function(String? translation) builder;
const TranslationWidget({
required this.message,
required this.fromLanguage,
required this.toLanguage,
required this.builder,
Key? key,
}) : super(key: key);
#override
_TranslationWidgetState createState() => _TranslationWidgetState();
}
class _TranslationWidgetState extends State<TranslationWidget> {
String? translation;
#override
Widget build(BuildContext context) {
// final fromLanguageCode = Translations.getLanguageCode(widget.fromLanguage);
final toLanguageCode = Translations.getLanguageCode(widget.toLanguage!);
return FutureBuilder(
future: TranslationApi.translate(widget.message!, toLanguageCode),
//future: TranslationApi.translate2(
// widget.message, fromLanguageCode, toLanguageCode),
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return buildWaiting();
default:
if (snapshot.hasError) {
translation = 'Could not translate due to Network problems';
} else {
translation = snapshot.data!;
}
return widget.builder(translation??"");
}
},
);
}
Widget buildWaiting() =>
translation == null ? Container() : widget.builder(translation??"");
}

How do I keep a variable value after UI state change?

So basically, I have an ImagePicker widget that empties every time my view changes in a browser(flutter web):
But once I change the size of the browser window, the image dissappears:
So I was wondering if there's a way to keep value of the variable that contains the string image.
This is the function that gets the image:
static Future<Uint8List?> webPickImage() async {
final picker = ImagePicker();
var file = await picker.pickImage(source: ImageSource.gallery);
if (file != null) {
return await file.readAsBytes();
} else {
return null;
}
}
As you can see, this returns an Uint8List which then is emited in a BLoC/cubit state management class variable
void userWebImageChanged() async {
final newWebImage = await CommonFunction.webPickImage();
emit(
state.copyWith(
webUserImage: newWebImage,
),
);
}
State management class:
So it appears that webUserImage loses it's value when the browser window changes size:
part of 'sign_up_cubit.dart';
class SignUpState extends Equatable {
const SignUpState(
{
this.webUserImage
});
final Uint8List? webUserImage;
#override
List<Object?> get props => [
webUserImage
];
SignUpState copyWith(
{
Uint8List? webUserImage
}) {
return SignUpState(
webUserImage: webUserImage ?? this.webUserImage);
}
}
Widget(read comments):
class SignUpForm extends StatelessWidget {
const SignUpForm({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => SignUpCubit(),
child: BlocConsumer<SignUpCubit, SignUpState>(
builder: (context, state) => ResponsiveRowColumn(
rowMainAxisAlignment: MainAxisAlignment.spaceEvenly,
rowPadding: const EdgeInsets.all(10),
columnPadding: const EdgeInsets.all(10),
layout: ResponsiveWrapper.of(context).isSmallerThan(DESKTOP)
? ResponsiveRowColumnType.COLUMN
: ResponsiveRowColumnType.ROW,
children: [
ResponsiveRowColumnItem(
rowFlex: 1,
child: SizedBox(
width: 350,
child: Column(
children: [
Wrap(
runSpacing: 10,
alignment: WrapAlignment.center,
children: [
ResponsiveVisibility(
hiddenWhen: const [
Condition.smallerThan(name: TABLET)
],
//BLoC
child: BlocBuilder<SignUpCubit, SignUpState>(
buildWhen: (previous, current) =>
previous.webUserImage !=
current.webUserImage,
builder: (cubitContext, state) {
return GestureDetector(
onTap: () async {
cubitContext
.read<SignUpCubit>()
.userWebImageChanged();
},
child: Container(
margin: const EdgeInsets.all(10),
width: 135,
height: 135,
decoration: BoxDecoration(
borderRadius:
const BorderRadius.all(
Radius.circular(10)),
border: Border.all(
color: Theme.of(context)
.primaryColorDark,
width: 1,
),
color: Colors.white),
child: state.webUserImage == null
? Center(
child: Icon(
Icons.add,
color: Theme.of(context)
.primaryColorDark,
size: 50,
),
)
: ClipRRect(
child: Image.memory(
// This is the state variable that sets and holds the image:
state.webUserImage!),
borderRadius:
const BorderRadius.all(
Radius.circular(11)),
),
),
);
},
),
//...

flutter reset only custom widget state after it's optional function execution

I have a page where we have some pickup session when you select a pickup session at the bottom SwipeActionButton widget activate
now user can swipe right side and after swipe complete an async function execute which most time hit an api so if api result is success app goes to next page no problem here but if api result gave an error it shows a dialog
Press Ok and dialog pop but SwipeActionButton widget still show complete swipe how I can reset it.
code
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: NormalAppBar(
title: Text("Assign Requests"),
),
body: Consumer<PickupSessionProvider>(
builder: (context, provider, child) => Stack(
children: [
widget.requestIds.length == 0
? _requestsLoaded
? provider.unassignedRequestCount == 0
? Center(
child: Text("No requests.",
style: Theme.of(context).textTheme.headline6),
)
: _buildRequestsList(provider.unassignedRequests!)
: Center(
child: CircularProgressIndicator(),
)
: provider.pickupSessions!.length == 0
? Center(
child: Text("No active pickup session.",
style: Theme.of(context).textTheme.headline6),
)
: ListView(
padding: const EdgeInsets.only(bottom: 80),
children: [
Padding(
padding: const EdgeInsets.all(20),
child: Text(
"Select pickup session",
style: Theme.of(context).textTheme.headline4,
),
),
for (var pickupSession in provider.pickupSessions!)
_buildPickupSessionTile(pickupSession)
],
),
Positioned(
bottom: 0,
child: SwipeActionButton(
margin: EdgeInsets.symmetric(horizontal: 15, vertical: 20),
onDone: (_selectRequestList && requestIds.length == 0) ||
(!_selectRequestList &&
_selectedPickupSessionId == null)
? null
: () async {
var result = await showDialog(
context: context,
builder: (context) => _ProgressDialog(
requestIds: requestIds,
pickupSessionId: _selectedPickupSessionId),
barrierDismissible: true);
if (result == true) Navigator.of(context).pop(true);
},
doneText: "Assign request",
disabledText: "Assign request",
infoText: "Swipe to assign request",
),
)
],
),
),
);
}
Custom SwipeActionButton widget
class SwipeActionButton extends StatefulWidget {
final double height;
final Color doneColor;
final Color swiperColor;
final Color textColor;
final String doneText;
final String disabledText;
final VoidCallback? onDone;
final String? infoText;
final double? width;
final Color? backgroundColor;
final EdgeInsetsGeometry margin;
SwipeActionButton({
Key? key,
this.height = 50,
this.doneColor = const Color(0xff44b31f),
this.swiperColor = const Color(0xff44b31f),
this.textColor = const Color(0xff44b31f),
required this.doneText,
required this.disabledText,
this.onDone,
this.infoText,
this.width,
this.backgroundColor,
this.margin = EdgeInsets.zero
}) : super(key: key);
#override
_SwipeActionButtonState createState() => _SwipeActionButtonState();
}
class _SwipeActionButtonState extends State<SwipeActionButton>
with SingleTickerProviderStateMixin {
double swipePercent = 0.0;
bool swipeDone = false;
bool isDisabled = false;
late Color backgroundColor;
late AnimationController _controller;
Animation<double>? _animation;
void initState() {
super.initState();
backgroundColor = widget.backgroundColor ?? Color(0xff3344b31f);
_controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 500))
..addListener(() {
setState(() {
swipePercent = _animation?.value ??0;
});
})
..addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.completed && swipeDone) {
widget.onDone!();
}
});
}
void dispose() {
_controller.dispose();
super.dispose();
}
_onDragStart(DragStartDetails details) {
_controller.reset();
swipePercent = 0.0;
}
_onDragUpdate(DragUpdateDetails details) {
setState(() {
swipePercent =
details.globalPosition.dx / MediaQuery.of(context).size.width;
if (swipePercent > 0.90) swipeDone = true;
});
}
_onDragEnd(DragEndDetails details) {
if (swipePercent > 0.90 || swipeDone) {
_animation =
Tween<double>(begin: swipePercent, end: 1).animate(_controller);
} else {
_animation =
Tween<double>(end: 0, begin: swipePercent).animate(_controller);
}
_controller.forward();
}
#override
Widget build(BuildContext context) {
isDisabled = widget.onDone == null;
double screenWidth = MediaQuery.of(context).size.width;
return Container(
alignment: Alignment.center,
margin: widget.margin,
width: screenWidth - widget.margin.horizontal,
child: Stack(
clipBehavior: Clip.hardEdge,
children: <Widget>[
Container(
height: widget.height,
decoration: BoxDecoration(
color: isDisabled ? Colors.grey : Color(0xff3344b31f),
borderRadius: BorderRadius.all(Radius.circular(100.0)),
border: Border.all(
color: isDisabled ? Colors.grey : Color(0xff3344b31f),
width: 1.5,
),
),
child: Center(
child: Text(widget.infoText ?? "",
style: Theme.of(context)
.textTheme
.subtitle1!
.copyWith(color: widget.textColor))),
),
Container(
width: isDisabled
? screenWidth
: lerpDouble(widget.height, screenWidth, swipePercent),
height: widget.height,
child: Center(
child: Opacity(
opacity: isDisabled ? 1 : lerpDouble(0, 1, swipePercent)!,
child: Text(
isDisabled ? widget.disabledText : widget.doneText,
style: Theme.of(context).textTheme.subtitle1!.copyWith(
color: Colors.white,
),
textScaleFactor:
isDisabled ? 1 : lerpDouble(0, 1, swipePercent),
))),
decoration: BoxDecoration(
color: isDisabled ? Colors.grey : null,
borderRadius: BorderRadius.all(Radius.circular(100.0)),
/* border: Border.all(
color: Theme.of(context).primaryColor,
width: 1.5,
), */
gradient: isDisabled
? null
: LinearGradient(
begin: Alignment.center,
end: Alignment.centerRight,
colors: [
widget.doneColor,
swipeDone ? widget.doneColor : backgroundColor
])),
),
isDisabled
? Container()
: Positioned(
left: lerpDouble(
0, screenWidth -(15 +widget.margin.horizontal) - (widget.height * .9), swipePercent),
/* top: widget.height * .1,
bottom: widget.height * .1,
*/
child: AbsorbPointer(
absorbing: swipeDone,
child: GestureDetector(
onHorizontalDragStart: _onDragStart,
onHorizontalDragUpdate: _onDragUpdate,
onHorizontalDragEnd: _onDragEnd,
child: Opacity(
opacity: 1,
child: AnimatedContainer(
duration: Duration(milliseconds: 500),
height: widget.height,
width: widget.height,
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(100.0)),
border: Border.all(
color: Theme.of(context).primaryColor,
width: 1.5,
),
boxShadow: swipeDone
? null
: [
BoxShadow(
color: Colors.black45,
blurRadius: 4)
],
color: swipeDone
? backgroundColor
: widget.swiperColor),
child: swipeDone
? Icon(
Icons.check,
size: 20,
color: Colors.white,
)
: Icon(
Icons.arrow_forward,
size: 20,
color: Colors.white,
),
))))),
],
),
);
}
}
You're asking how you can make the swipeButton reset in case the request doesn't return with a valid value.
The swipeButton's state is defined by its swipeDone and swipePercent variables. To achieve what you want you need to pass swipeDone as a parameter when constructing the widget.
class SwipeActionButton extends StatefulWidget {
// Make swipeDone a class variable for the widget
bool swipeDone;
final double height;
final Color doneColor;
final Color swiperColor;
final Color textColor;
final String doneText;
final String disabledText;
final VoidCallback? onDone;
final String? infoText;
final double? width;
final Color? backgroundColor;
final EdgeInsetsGeometry margin;
SwipeActionButton({
// Add it to the constructor
required this.swipeDone,
Key? key,
this.height = 50,
this.doneColor = const Color(0xff44b31f),
this.swiperColor = const Color(0xff44b31f),
this.textColor = const Color(0xff44b31f),
required this.doneText,
required this.disabledText,
this.onDone,
this.infoText,
this.width,
this.backgroundColor,
this.margin = EdgeInsets.zero,
}) : super(key: key);
#override
_SwipeActionButtonState createState() => _SwipeActionButtonState();
}
In _SwipeActionButtonState, delete bool swipeDone = false; and replace every swipeDone by widget.swipeDone.
You also need to reset the value of swipePercent.
You can do this by adding at the beginning of the swipeButton's build method :
if (widget.swipeDone == false && swipePercent > 0.9) swipePercent = 0;
Now you can declare the variable swipeDone in the parent widget state, pass it as a parameter and modify it whenever needed. For more clarity I give you an example with a simple widget that reset the swipe button when the floating action button is pressed.
Complete code :
import 'package:flutter/material.dart';
import 'dart:ui';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool swipeDone = false;
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(),
body: SwipeActionButton(
disabledText: 'disabled',
doneText: 'doneText',
onDone: () {},
swipeDone: swipeDone,
),
floatingActionButton: FloatingActionButton(onPressed: () {
setState(() {
swipeDone = false;
});
}),
),
);
}
}
class SwipeActionButton extends StatefulWidget {
// Make swipeDone a class variable for the widget
bool swipeDone;
final double height;
final Color doneColor;
final Color swiperColor;
final Color textColor;
final String doneText;
final String disabledText;
final VoidCallback? onDone;
final String? infoText;
final double? width;
final Color? backgroundColor;
final EdgeInsetsGeometry margin;
SwipeActionButton({
// Add it to the constructor
required this.swipeDone,
Key? key,
this.height = 50,
this.doneColor = const Color(0xff44b31f),
this.swiperColor = const Color(0xff44b31f),
this.textColor = const Color(0xff44b31f),
required this.doneText,
required this.disabledText,
this.onDone,
this.infoText,
this.width,
this.backgroundColor,
this.margin = EdgeInsets.zero,
}) : super(key: key);
#override
_SwipeActionButtonState createState() => _SwipeActionButtonState();
}
class _SwipeActionButtonState extends State<SwipeActionButton> with SingleTickerProviderStateMixin {
double swipePercent = 0.0;
bool isDisabled = false;
late Color backgroundColor;
late AnimationController _controller;
Animation<double>? _animation;
#override
void initState() {
super.initState();
backgroundColor = widget.backgroundColor ?? Color(0xff3344b31f);
_controller = AnimationController(vsync: this, duration: Duration(milliseconds: 500))
..addListener(() {
setState(() {
swipePercent = _animation?.value ?? 0;
});
})
..addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.completed && widget.swipeDone) {
widget.onDone!();
}
});
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
_onDragStart(DragStartDetails details) {
_controller.reset();
swipePercent = 0.0;
}
_onDragUpdate(DragUpdateDetails details) {
setState(() {
swipePercent = details.globalPosition.dx / MediaQuery.of(context).size.width;
if (swipePercent > 0.90) widget.swipeDone = true;
});
}
_onDragEnd(DragEndDetails details) {
if (swipePercent > 0.90 || widget.swipeDone) {
_animation = Tween<double>(begin: swipePercent, end: 1).animate(_controller);
} else {
_animation = Tween<double>(end: 0, begin: swipePercent).animate(_controller);
}
_controller.forward();
}
#override
Widget build(BuildContext context) {
if (widget.swipeDone == false && swipePercent > 0.9) swipePercent = 0;
isDisabled = widget.onDone == null;
double screenWidth = MediaQuery.of(context).size.width;
return Container(
alignment: Alignment.center,
margin: widget.margin,
width: screenWidth - widget.margin.horizontal,
child: Stack(
clipBehavior: Clip.hardEdge,
children: <Widget>[
Container(
height: widget.height,
decoration: BoxDecoration(
color: isDisabled ? Colors.grey : Color(0xff3344b31f),
borderRadius: BorderRadius.all(Radius.circular(100.0)),
border: Border.all(
color: isDisabled ? Colors.grey : Color(0xff3344b31f),
width: 1.5,
),
),
child: Center(
child: Text(widget.infoText ?? "",
style: Theme.of(context).textTheme.subtitle1!.copyWith(color: widget.textColor))),
),
Container(
width: isDisabled ? screenWidth : lerpDouble(widget.height, screenWidth, swipePercent),
height: widget.height,
child: Center(
child: Opacity(
opacity: isDisabled ? 1 : lerpDouble(0, 1, swipePercent)!,
child: Text(
isDisabled ? widget.disabledText : widget.doneText,
style: Theme.of(context).textTheme.subtitle1!.copyWith(
color: Colors.white,
),
textScaleFactor: isDisabled ? 1 : lerpDouble(0, 1, swipePercent),
))),
decoration: BoxDecoration(
color: isDisabled ? Colors.grey : null,
borderRadius: BorderRadius.all(Radius.circular(100.0)),
/* border: Border.all(
color: Theme.of(context).primaryColor,
width: 1.5,
), */
gradient: isDisabled
? null
: LinearGradient(
begin: Alignment.center,
end: Alignment.centerRight,
colors: [widget.doneColor, widget.swipeDone ? widget.doneColor : backgroundColor])),
),
isDisabled
? Container()
: Positioned(
left:
lerpDouble(0, screenWidth - (15 + widget.margin.horizontal) - (widget.height * .9), swipePercent),
/* top: widget.height * .1,
bottom: widget.height * .1,
*/
child: AbsorbPointer(
absorbing: widget.swipeDone,
child: GestureDetector(
onHorizontalDragStart: _onDragStart,
onHorizontalDragUpdate: _onDragUpdate,
onHorizontalDragEnd: _onDragEnd,
child: Opacity(
opacity: 1,
child: AnimatedContainer(
duration: Duration(milliseconds: 500),
height: widget.height,
width: widget.height,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(100.0)),
border: Border.all(
color: Theme.of(context).primaryColor,
width: 1.5,
),
boxShadow:
widget.swipeDone ? null : [BoxShadow(color: Colors.black45, blurRadius: 4)],
color: widget.swipeDone ? backgroundColor : widget.swiperColor),
child: widget.swipeDone
? Icon(
Icons.check,
size: 20,
color: Colors.white,
)
: Icon(
Icons.arrow_forward,
size: 20,
color: Colors.white,
),
))))),
],
),
);
}
}

how to disable tooltip dynamcically in flutter?

I can disable the tooltip statically.
But I want to disable tooltip dynamically when i click flatbutton.But Couldnt disable dynamically and i have no idea to do that.
This is my code:
import 'package:flutter/material.dart';
void main(){
runApp(MaterialApp(home: HelloWorld(),debugShowCheckedModeBanner: false,));
}
class HelloWorld extends StatefulWidget {
#override
_HelloWorldState createState() => _HelloWorldState();
}
class _HelloWorldState extends State<HelloWorld> {
bool check = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(children: <Widget>[
TopToolbar(),
FlatButton(
child: Text("Disable Tooltip"),
onPressed: () {
setState(() {
TopToolbar toolbar = new TopToolbar();
toolbar.showTooltip = false;
});
},
),
]),
),
));
}
}
class TopToolbar extends StatefulWidget {
bool showTooltip;
final Color backgroundColor;
final double height;
bool isVisible;
TopToolbar({
this.height = 55,
this.isVisible = true,
this.backgroundColor = const Color(0xFFEEEEEE),
Key key,this.showTooltip=true,
}) : super(key: key);
#override
_TopToolbarState createState() => _TopToolbarState();
}
class _TopToolbarState extends State<TopToolbar> {
#override
Widget build(BuildContext context) {
if (widget.isVisible) {
return Container(
foregroundDecoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.grey,
),
),
),
margin: EdgeInsets.only(bottom: 1),
color: widget.backgroundColor,
height: widget.height,
child: Stack(
children: <Widget>[
Positioned(
top: 7,
right: 60,
height: 40,
width: 40,
child: RawMaterialButton(
elevation: 0.0,
fillColor: widget.backgroundColor,
splashColor: Colors.grey[300],
child: IconButton(
icon: Icon(
Icons.bookmark,
color: Colors.grey[500],
size: 25,
),
onPressed: (){},
tooltip: widget.showTooltip ? "Bookmark" : null,
),
onPressed: (){},
),
),
],
),
);
} else {
return Container();
}
}
}
If I give statically false. it works fine.
For example : If add child like TopToolbar(showTooltip : false),it works fine,
But If i give toolbar.showTooltip = false in Flatbutton onPressed method,it doesnt work.
I want to disble it in dynamically. please help me to do that.
we can hide or deactivate tooltip programmatically like below,
Future.delayed(
Duration(seconds: 2),
() {
tooltip?.deactivate();
}
);
Here, we can set time according to your requirement.(Currently, we are set 2 sec.)
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: HelloWorld(),
debugShowCheckedModeBanner: false,
));
}
class HelloWorld extends StatefulWidget {
#override
_HelloWorldState createState() => _HelloWorldState();
}
class _HelloWorldState extends State<HelloWorld> {
bool check = false;
bool showTooltip = true;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(children: <Widget>[
TopToolbar(showTooltip: showTooltip),
FlatButton(
child: Text("Disable Tooltip"),
onPressed: () {
setState(() {
showTooltip = false;
});
},
),
]),
),
));
}
}
class TopToolbar extends StatefulWidget {
final bool showTooltip;
final Color backgroundColor;
final double height;
final bool isVisible;
TopToolbar({
this.height = 55,
this.isVisible = true,
this.backgroundColor = const Color(0xFFEEEEEE),
Key key,
this.showTooltip = true,
}) : super(key: key);
#override
_TopToolbarState createState() => _TopToolbarState();
}
class _TopToolbarState extends State<TopToolbar> {
#override
Widget build(BuildContext context) {
if (widget.isVisible) {
return Container(
foregroundDecoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.grey,
),
),
),
margin: EdgeInsets.only(bottom: 1),
color: widget.backgroundColor,
height: widget.height,
child: Stack(
children: <Widget>[
Positioned(
top: 7,
right: 60,
height: 40,
width: 40,
child: RawMaterialButton(
elevation: 0.0,
fillColor: widget.backgroundColor,
splashColor: Colors.grey[300],
child: IconButton(
icon: Icon(
Icons.bookmark,
color: Colors.grey[500],
size: 25,
),
onPressed: () {},
tooltip: widget.showTooltip ? 'Bookmark' : null,
),
onPressed: () {},
),
),
],
),
);
} else {
return Container();
}
}
}
I've used this method to hide tooltips:
Tooltip(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0),
),
textStyle: TextStyle(color: Colors.white.withOpacity(0)),
message: 'Certificates',
child: Container()
);
Make the property message='' // empty string
setState((){messageText=''});
Tooltip(
message: messageText,
...
)