I'm trying to use a bloc to check valid input and make an ElevatedButton(the new implementation for RaisedButton) either enabled or disabled. However, the enabled flag seems to not be implemented in the ElevatedButton source code despite it being shown in the docs as a constructor parameter. Was this changed and the docs not updated? I'm on flutter 1.22.4.
For context, I am using a global ElevatedButton widget for my app. Passing a null function to it caused it to cause an error. So I need the enabled flag to manually set the button enabled or not.
/// Get a custom raised button
Widget getRaisedButton(
{String buttonText,
dynamic icon,
Color buttonColor,
Color textColor,
dynamic colors,
Function onPressed,
EdgeInsetsGeometry padding,
/// A widget to be displayed instead of the button text
Widget buttonWidget,
ButtonStyle buttonStyle}) {
return ElevatedButton(
onPressed: onPressed(),
style: ButtonStyle(
visualDensity: VisualDensity.adaptivePlatformDensity,
backgroundColor: MaterialStateProperty.all<Color>(buttonColor),
padding: MaterialStateProperty.all<EdgeInsetsGeometry>(padding))
.merge(buttonStyle),
child: icon != null
? Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(padding: EdgeInsets.only(left: 8)),
icon,
Spacer(),
Text(
buttonText,
style: defaultTextStyle(
textColor != null ? textColor : colors.mainTextColor),
),
Spacer()
],
)
: buttonWidget != null
? buttonWidget
: Text(buttonText,
style: defaultTextStyle(
textColor != null ? textColor : colors.mainTextColor)),
);
}
Attempt to pass null
StreamBuilder<bool>(
stream: bloc.inputValid,
builder: (context, snapshot) {
return getRaisedButton(
colors: colors,
buttonText: continueText,
buttonColor: colors.mainButtonsColor,
textColor: colors.mainBackgroundColor,
buttonWidget: authenticationState.isAuthenticating
? CircularProgressIndicator(
backgroundColor: colors.mainBackgroundColor,
)
: null,
padding: EdgeInsets.symmetric(vertical: 12, horizontal: 100),
onPressed: !snapshot.hasData
? null
: () async {
final form = _formKey.currentState;
if (form.validate()) {
form.save();
//TODO: Use dependency injection for these
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
AndroidDeviceInfo androidInfo =
await deviceInfo.androidInfo;
if (androidInfo.isPhysicalDevice) {
context
.read(authenticationProvider)
.setFullPhoneNumber(
"${authenticationState.phoneNumberPrefix}${_user.phoneNumber}");
context
.read(authenticationProvider)
.verifyPhoneNumber(context,
"${authenticationState.phoneNumberPrefix}${_user.phoneNumber}");
} else {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => OtpPage()));
}
}
});
}),
Noticed the elevated flag is read-only. I will pass null to the function anyway, if it causes an app breaking error I will have to use a separate Elevated button here instead of the global one then pass null to the onPressed function.
Related
I have a DataTable in which some cells have links. Ideally, I would like to fetch a preview about the link's content whenever hovering over the link, which I was able to achieve using the Stack widget. However, since the stacked preview is inside the DataCell, it seems like I'm not able to raise its "z-index" to be on top of the rest of the table.
Is this not possible with Flutter, or is there a way around it?
The only way I imagine this working, without something to update a global z-index, would be for the cell to update a global state and then have the thumbnail preview appear on a Stack above the DataTable level. But I wish there was a less clunkier way to do it...
3 widgets I've tried but to no avail — they might work, I don't know —:
Tooltip
Overlay
FloatingActionButton
My whole app is here, and the precise commit is 0303732. The relevant code is this ClickableLink widget:
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
import 'package:url_launcher/url_launcher.dart';
import '../schema/links.dart';
#immutable
class ClickableLink extends StatefulWidget {
const ClickableLink({
Key? key,
required this.link,
this.linkText,
this.color = Colors.blue,
}) : super(key: key);
final Link link;
final String? linkText;
final Color color;
#override
State<ClickableLink> createState() => _ClickableLinkState();
}
class _ClickableLinkState extends State<ClickableLink> {
Widget hoverWidget = const SizedBox.shrink();
void _fetchPreview(PointerEvent pointerEvent) {
setState(() {
if (widget.link.host == 'online-go.com' && widget.link.prePath == 'game') {
hoverWidget = Positioned(
top: 25,
child: Image.network('https://online-go.com/api/v1/games/${widget.link.id}/png'),
);
}
});
}
void _onExit(PointerEvent pointerEvent) {
setState(() {
hoverWidget = const SizedBox.shrink();
});
}
#override
Widget build(BuildContext context) {
return MouseRegion(
onHover: _fetchPreview,
onExit: _onExit,
child: Stack(
clipBehavior: Clip.none,
children: [
SelectableText.rich(
TextSpan(
text: widget.linkText ?? widget.link.id,
style: TextStyle(color: widget.color),
recognizer: TapGestureRecognizer()
..onTap = () async => launch(widget.link.completeLink),
),
),
hoverWidget,
],
),
);
}
}
The problem here is due to the fact that your Stack widget, defined inside ClickableLink, will be at a "lower" point (inside your app widget tree) than every other GameResultCell.
So even the higher z-index will still be behind the other GameResultCells.
To fix this I would reccomend changing your structure and define an higher point in your structure to show the preview.
Another way could be using a library to nest your preview inside a tooltip. Take a look at this one for example:
just_the_tooltip: ^0.0.11+2. With this package, you could even use a StatelessWidget.
The result here is more similar to what I suppose you were expecting.
class ClickableLink extends StatelessWidget {
#override
Widget build(BuildContext context) {
return JustTheTooltip(
content: Image.network(
'https://online-go.com/api/v1/games/${widget.link.id}/png',
),
child: SelectableText.rich(
TextSpan(
text: widget.linkText ?? widget.link.id,
style: TextStyle(
color: widget.color ??
(DogempTheme.currentThemeIsLight(context)
? const Color(0xff1158c7)
: Colors.orange.withOpacity(0.85)),
),
recognizer: TapGestureRecognizer()
..onTap = () async => launch(widget.link.completeLink),
),
),
);
}
}
Lastly you could use a Dialog, but the resulting behaviour is a bit different.
Take a look at this code if you want to try:
class _ClickableLinkState extends State<ClickableLink> {
Widget hoverWidget = const SizedBox.shrink();
void _fetchPreview(PointerEvent pointerEvent) {
showDialog(
context: context,
builder: (context) {
return Dialog(
backgroundColor: Colors.transparent,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.network(
'https://online-go.com/api/v1/games/${widget.link.id}/png'),
const SizedBox(
height: 16.0,
),
TextButton(
onPressed: () async => launch(widget.link.completeLink),
child: const Text('Go to complete link'))
],
),
);
},
);
}
#override
Widget build(BuildContext context) {
return MouseRegion(
onHover: _fetchPreview,
child: Stack(
clipBehavior: Clip.none,
children: [
SelectableText.rich(
TextSpan(
text: widget.linkText ?? widget.link.id,
style: TextStyle(
color: widget.color ??
(DogempTheme.currentThemeIsLight(context)
? const Color(0xff1158c7)
: Colors.orange.withOpacity(0.85)),
),
recognizer: TapGestureRecognizer()
..onTap = () async => launch(widget.link.completeLink),
),
),
],
),
);
}
}
I have used a package group_button 4.2.1 but once i select the textfields the radio buttons deselect and i have to select again, i have tried using the controller property of the Widget but i didn't get it to work.
I was thinking if i can make a container from scratch that is a radio button and can retain the value once i finish filling the form to be submitted to my firestore database.
You can use List of button text and keep tract of selectedIndex.
Run on dartPad
int? _selectedValueIndex;
List<String> buttonText = ["ForSale", "For rent"];
Widget button({required String text, required int index}) {
return InkWell(
splashColor: Colors.cyanAccent,
onTap: () {
setState(() {
_selectedValueIndex = index;
});
},
child: Container(
padding: const EdgeInsets.all(12),
color: index == _selectedValueIndex ? Colors.blue : Colors.white,
child: Text(
text,
style: TextStyle(
color: index == _selectedValueIndex ? Colors.white : Colors.black,
),
),
),
);
}
Inside build method to use this,
Row(
children: [
...List.generate(
buttonText.length,
(index) => button(
index: index,
text: buttonText[index],
),
)
],
),
I wrote a code that after logging in with the MaterialPageRoute command takes you to the "main" page. But I'm afraid that you can also enter without logging in, by changing the url. Do you know how I can solve? if it is not clear what I want to do ask in the comments, however I would like to avoid that through the url from the login you can go to the main page
FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(2)),
padding: EdgeInsets.fromLTRB(widthButton, 5, widthButton, 5),
color: Colors.white,
//Verifica del nome utente e password che se corretto porta alla pagina princiapale
onPressed: () async {
var username = _usernameController.text;
var password = _passwordController.text;
var jwt = await attemptLogIn(username, password);
if (jwt != '') {
window.localStorage["csrf"] = jwt;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomePage.fromBase64(jwt)));
On your non-protected screens
Maybe use the Offstage widget to deliver a message asking the user to login with a link to redirect them to the login page. On the offstage: argument pass a bool that is only true if the user successfully logs into the app.
final GlobalKey _key = GlobalKey();
bool _isLoggedIn = true;
Size _getFlutterLogoSize() {
final RenderBox renderLogo = _key.currentContext!.findRenderObject()! as RenderBox;
return renderLogo.size;
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Offstage(
offstage: _isLoggedIn,
child: FlutterLogo(
key: _key,
size: 150.0,
),
),
Text('Flutter logo is offstage: $_offstage'),
ElevatedButton(
child: const Text('Toggle Offstage Value'),
onPressed: () {
setState(() {
_isLoggedIn = !_isLoggedIn;
});
},
),
if (_isLoggedIn)
ElevatedButton(
child: const Text('Get Flutter Logo size'),
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Flutter Logo size is ${_getFlutterLogoSize()}'),
),
);
}
),
],
);
}
Source of sample code on api.flutter.dev
Try the sample code in the website above by running the code and clicking the buttons that show and hide the logo. You need to figure out how this would fit into your code since you are only showing a small part of the code.
I'm trying to create a simple vertical scrolling calendar.
Problem is that I can't manage to find a way to reset back to previous state in case I tap on a new container.
Here's the code:
class CalendarBox extends StatelessWidget {
BoxProprieties boxProprieties = BoxProprieties();
Map item;
CalendarBox({this.item});
bool selected = false;
#override
Widget build(BuildContext context) {
return Consumer<Producer>(
builder: (context, producer, child) => GestureDetector(
onTap: () {
print(item['dateTime']);
selected = producer.selectedState(selected);
},
child: AnimatedContainer(
duration: Duration(milliseconds: 100),
color: selected == true ? Colors.blue : Colors.grey[200],
height: 80,
width: 50,
margin: EdgeInsets.only(top: 5),
child: Column(
children: [
Text(
'${item['dayNum']}',
style: TextStyle(
fontWeight: FontWeight.bold,
color: boxProprieties.dayColor(item['dateTime'])),
),
],
),
),
),
);
}
}
Here's the situation:
One way to achieve it is, create a model for boxes and keep a value current selected block, in your model you will have the index assigned to that block,
int currentSelected =1; //initial value
class Block{
int id;
..
.. // any other stuff
}
now in your code, the check modifies to
block.id == currentSelected ? Colors.blue : Colors.grey[200],
your on tap modifies to
onTap: () {
setState(){
currentSelected = block.id
};
},
If you want to prevent the rebuild of the whole thing every time you can use valueNotifire for current selected block. Hope this gives you an idea.
Yesterday I spent over ten hours trying to learn a bit of MobX and applying a simple SnackBar if there is an error coming from the API. My question is if the solution I found can be considered good and appropriate or there is a better one to be implemented.
class _LoginPageState extends State<LoginPage> {
final _scaffoldKey = GlobalKey<ScaffoldState>();
final _controller = Modular.get<LoginController>();
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text(widget.title),
),
body: Observer(
builder: (context) {
if (_controller.token?.error != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text(_controller.token?.error),
duration: Duration(seconds: 2),
));
});
}
return Center(
child: PrimaryButton(
onPressed: () => _controller.authenticate(),
text: 'Enviar',
icon: Icons.send,
),
);
},
),
);
}
}
In case you're curious about it, I'm using flutter_modular, hence the Modular.get<>()
I like this approach, that is as long as you make sure your snackbar does NOT cover the content of the page, as you know errors from API's could be complex and well documented, therefore you may come across a situation where the snackbar would cover your content.
I usually would use showDialog instead, as errors should not usually accur. when they do I would push a popup displaying and explaining the situation using the error details.
This is my customized version of popups:
class ButtonsAndAction extends FlatButton{
///Providing a func is "optional", just pass null if you want the defualt action to pop the navigator.
ButtonsAndAction(BuildContext context, String text, Function func ) : super(child: new Text(text, textDirection: Helper.textDirection(),style: TextStyle(color: ConstantValues.mainBackgroundColor)
,), onPressed: func == null ? () {Navigator.of(context).pop();} : func);
}
class Helper{
static TextDirection textDirection() => AppConfig.rtl ? TextDirection.rtl : TextDirection.ltr;
/// Used to push alerts of texts with a set of actions(buttons and actions) if wanted
static Future pushDialog(BuildContext context, String title, String body, {List<ButtonsAndAction> actions, bool dismissable = true}) {
return showDialog(
context: context,
builder: (BuildContext context) {
return new WillPopScope(
onWillPop: () async => dismissable,
child:
new AlertDialog(
shape: new RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(ConstantValues.roundRadius)),
side: BorderSide(color: ConstantValues.mainBackgroundColor, width: ConstantValues.roundBorderWidthForPopup)),
title: new Container(child: new Text(title, textDirection: textDirection(), style: TextStyle(color: ConstantValues.mainBackgroundColor),), width: double.infinity,),
content: new Container(child: SingleChildScrollView(child:
new Text(body, textDirection: textDirection(), style: TextStyle(color: ConstantValues.mainBackgroundColor))),
width: double.infinity),
actions: actions
));
},
);
}
}
Good luck!