How can I disable the default splash/ripple/ink effect on a Widget? Sometimes the effect is unwanted, such as in the following TextField case:
Per #hunter's suggestion above, I found that by setting both highlightColor and splashColor in my theme to Colors.transparent removed the ripple.
I do hold some concerns that setting highlightColor might have some knock-on effects, but I haven't noticed any yet.
Edit: While my original answer has loads of up-votes, the more I learn, the more I've realised that it really isn't the right way to do it. As several people have pointed out below, a better solution is to use the splashFactory. For example, the code below shows it being set directly via the style, or you can set it in your theme too:
ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
splashFactory: NoSplash.splashFactory,
),
child: child,
);
You can wrap the component into Theme and set the properties splashColor and highlightColor to transparent on ThemeData
Theme(
data: ThemeData(
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
),
child: YourWidget(),
);
You can replace the Theme's splashFactory with one that doesn't paint anything:
class NoSplashFactory extends InteractiveInkFeatureFactory {
const NoSplashFactory();
#override
InteractiveInkFeature create({
MaterialInkController controller,
RenderBox referenceBox,
Offset position,
Color color,
TextDirection textDirection,
bool containedInkWell = false,
Rect Function() rectCallback,
BorderRadius borderRadius,
ShapeBorder customBorder,
double radius,
VoidCallback onRemoved,
}) {
return NoSplash(
controller: controller,
referenceBox: referenceBox,
);
}
}
class NoSplash extends InteractiveInkFeature {
NoSplash({
#required MaterialInkController controller,
#required RenderBox referenceBox,
}) : assert(controller != null),
assert(referenceBox != null),
super(
controller: controller,
referenceBox: referenceBox,
);
#override
void paintFeature(Canvas canvas, Matrix4 transform) {}
}
And wrap your widget with it:
child: new Theme(
data: new ThemeData(splashFactory: const NoSplashFactory()),
child: new TextField(...),
),
Originally answered by HansMuller on a GitHub PR.
Use NoSplash.splashFactory
Set to a theme
final yourTheme = ThemeData.light();
...
Theme(
data: yourTheme.copyWith(
splashFactory: NoSplash.splashFactory,
),
...
)
Set to a material widget
ElevatedButton(
style: ElevatedButton.styleFrom(
splashFactory: NoSplash.splashFactory,
),
onPressed: () { },
child: Text('No Splash'),
)
I'll modify Camilo's approach just to be sure we don't override other properties of the parent theme.
var color = Colors.transparent;
Theme(
data: Theme.of(context).copyWith(
highlightColor: color,
splashColor: color,
hoverColor: color,
),
child: YourWidget(),
)
I have tried the above answer without success(splashColor: Colors.transparent, highlightColor: Colors.transparent,).
My solution was to only set hoverColor:Colors.transparent
As I was looking for a way to remove the slash from list overscroll, none of the ThemeData related solutions worked for me. I thought that this question was the same as the one I had, so for any users facing the same misunderstanding, the following solution proved to work, and is pretty neat when put into a stateless widget as you can see below:
class NoSplash extends StatelessWidget {
NoSplash({this.child});
final Widget child;
#override
Widget build(BuildContext context) {
return NotificationListener<OverscrollIndicatorNotification>(
onNotification: (OverscrollIndicatorNotification overscroll) {
overscroll.disallowGlow();
return true;
},
child: child);
}
}
The way to use this is to simply wrap your widget with NoSplash(child: )
Hope someone finds this useful!
I found this question while looking for a solution to disable splash on ElevatedButton. all solutions presented here did not work for my problem, even though Theme(data: ThemeData(... NoSplash..)) was working but for some reason did not work. I set overlayColor:.. in ButtonStyle() to transparent like this: overlayColor: MaterialStateProperty.all(Colors.transparent), and worked. hope this will help someone
Related
I have designed a flutter screen, in which I have button upon pressing that Button A I get a popup window, which has Button 1 to add new textfield, and we can add any number of textfield, for this I have used flutter_form_bloc dependency example. Then in the popup window there is another button, ie Button 2, which upon pressed process the data entered into the textfields and exits the popup window.
Now when again I press the Button A to open the popup window all the textfields are gone and so the data. I don't want that to happen. I want that those should there until the main flutter screen is there or not exited.
Moreover, upon pressing the Button 2 in the popup window the data should be passed to the class of the main flutter screen in which Button Ais there and should stored in a instance so that the data passed could be processed further.
Here are the screenshots to get the idea
[Image 1]1 [Image 2]2
CODE
FormBlocListener<ListFieldFormBloc2, String, String>(
onSubmitting: (context, state) {
},
onSuccess: (context, state) {
String name1;
var parsedData = json.decode(state.successResponse);
List members = parsedData['members'];
members.forEach((member){
name1 = member['step'];
List<String> _step = [];
_step.add(member["step"]);
_AddStepsState().getsteps(members);
});
_AddStepsState(steps: members);
Navigator.pop(context);
},
onFailure: (context, state) {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text(state.failureResponse)));
},
child: SingleChildScrollView(
physics: ClampingScrollPhysics(),
child: Column(
children: <Widget>[
BlocBuilder<ListFieldBloc<MemberFieldBloc2>,
ListFieldBlocState<MemberFieldBloc2>>(
bloc: formBloc.members,
builder: (context, state) {
if (state.fieldBlocs.isNotEmpty) {
return ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: state.fieldBlocs.length,
itemBuilder: (context, i) {
return MemberCard2(
memberIndex: i,
memberField: state.fieldBlocs[i],
onRemoveMember: () =>
formBloc.removeMember(i),
);
},
);
}
return Container();
},
),
RaisedButton(
color: Colors.blue[100],
onPressed: formBloc.addMember,
child: Text('ADD STEP'),
),
],
),
),
),
I tried to pass the LIST generated to another class in these ways _AddStepsState(steps: members); and _AddStepsState().getsteps(members); but both time it failed.
I want to the the list of the values in the text field generated to be passed to another class
And Also I want that while the user is in Screen1 as in Image 1 if the fields are edited as in image 2 and if the user opens the popup screen again the fields should remain there and not removed.
How should I achieve it?
if any more information is required , please let me know
the link to the dependency used is here flutter form bloc
I have just recently created an app that deals with a lot of forms and the solution I've gone with is as follows:
Have a file called data_center.dart where you can define classes to represent data created by a form / to be displayed in a "review" page.
class MyFormData{
String firstField;
String secondField;
int quantity;
// more attributes
// optional constructor
MyFormData({this.firstField, this.secondField, this.quantity});
}
Create a variable to hold instances of the class in data_center.dart
MyFormData currentMyFormDataInstance; // assign an instance every time a new form starts
Create instance in dialog (for example)
import 'data_center.dart' as DataCenter;
// code removed for brevity
return showDialog(
context: context,
builder: (BuildContext context) {
// create an instance to hold the current form data
currentMyFormDataInstance = DataCenter.MyFormData();
return AlertDialog(...);
]);
});
Store controller (assume we have a TextFormField) value in instance
TextFormField(
controller: _myController,
onChanged: (String _incomingValue){
DataCenter.currentMyFormDataInstance.firstField = _incomingValue;
}
)
I don't know if there are any critical flaws or inefficiencies that might come along but so far, it has worked very well for me as it allows me to easily manage all the different kinds of data groups I am collecting from the UI.
Moreover, storing these data as objects rather than data types such as Maps has allowed me to easily transform them by adding named constructors or extra methods that easily allows me to do common and frequent operations on my data.
For example, if you are using Cloud Firestore. I can add the following named constructor to easily map DocumentSnapshots to the class attributes.
MyFormData.fromDocumentSnapshot(){...}
I am using an HTML element view from dart:html to display a webpage inside my flutter web app. It catches all the touches in its area, including the ones on the FAB above it, and also the ones on the drawer of the scaffold in context. I don't even need touch input on the webview, I just want to display it. Also, note that absorbpointer and ignorepointer do not solve the problem. Here is the code displaying the webpage, inside the body of the scaffold.
final IFrameElement _iframeElement = IFrameElement();
_iframeElement.src = "webpageurl";
_iframeElement.style.border = 'none';
// ignore: undefined_prefixed_name
ui.platformViewRegistry.registerViewFactory(
'iframeElement',
(int viewId) => _iframeElement,
);
Widget _iframeWidget;
_iframeWidget = HtmlElementView(
key: UniqueKey(),
viewType: 'iframeElement',
);
return Center(child: IgnorePointer(child: _iframeWidget));
Edit:
final IFrameElement _iframeElement = IFrameElement();
_iframeElement.src = "https://index.hu/";
_iframeElement.style.border = 'none';
// ignore: undefined_prefixed_name
ui.platformViewRegistry.registerViewFactory(
'iframeElement',
(int viewId) => _iframeElement,
);
Widget _iframeWidget;
_iframeWidget = HtmlElementView(
key: UniqueKey(),
viewType: 'iframeElement',
);
return Stack(
children: <Widget>[
IgnorePointer(
ignoring: true,
child: Center(
child: _iframeWidget,
),
),
Container(
color: Colors.transparent,
),
],
);
PointerInterceptor is a widget that prevents mouse events (in web) from being captured by an underlying HtmlElementView.
If you are still struggling with PointerInterceptor, you can look at DropzoneView from the package flutter_dropzone. When stacked above the iFrame it prevented the clicks from being captured by the underlying iFrame.
I created a conditional stack element that placed this DropzoneView when I needed this behavior. I just could not get PointerInterceptor to work.
This is worth a try. Maybe this will help - comment with your experience here.
I’m trying to represent HTML code in Flutter (ul to be specific) and minimally style it. I tried using the flutter_html package, and I see there’s a customRender option.
Basically, what I’m trying to do:
Add a space under every li element.
Take away the left margin (it's there by default on flutter_html).
Change the color of the bullet point. Not, necessary, but would be nice.
I'm pretty new to Flutter and Dart, so I can't manage to make the customRender work. Perhaps someone knows how to go around it? Or maybe there's a better way to do it?
Adding some code fore reference.
import 'package:flutter_html/flutter_html.dart';
// ... Some non-essential code ...
Align(
alignment: Alignment.centerLeft,
child: Html(
data: """${unorderedList}""",
customRender: (node, children) {
if (node is dom.Element) {
switch (node.localName) {
case "li":
return Column(children: children);
}
}
return null;
},
)
)
Note: taking out the customRender block produces a left margin.
EDIT: Addition to the vb10 solution that allows multiline list item texts.
Wrap customListItem(dom.Element node) {
return Wrap(
spacing: 25.0,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
// Top padding adjusts for text offset, set it to the font size.
padding: const EdgeInsets.only(top: 18.0, right: 15.0),
child: CircleAvatar(radius: 4)
),
Expanded(child: Text(node.text)),
]
),
SizedBox(height: 25.0),
]
);
}
You don't forget two rules:
You can create custom widget so rich-text set to false (default True)
You have custom widget options need page margin like item spacing.
Look at this code:
Html(
data: data,
useRichText: false,
customRender: htmlCustomRenderer,
)
If you detail look at this.(lib/html-parser)
Result:
The package has been updated (v1.0.0) and it has been changed but it's now easier to do:
Html(
data: {html}
customRender: {
"li": (RenderContext context, Widget child, attributes, dom.Element element) {
return this.customListItem(element);
},
},
),
Wrap customListItem(dom.Element node) {
return Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
spacing: 4,
children: [
Padding(
padding: EdgeInsets.only(left: 10),
child: CircleAvatar(
radius: 2,
),
),
Text(node.text)
],
);
}
This one seems really simply conceptually, but doesn't exist in any of the beginner guides I'm reading through. Basically I'm wanting to dynamically change my UI based on user input, as test I set up a function outside of this:
testFunction(){
return Container(
child: Text(
"Hello World"
),
);
}
dynamic outputType1 = testFunction;
...with the intention of having multiple versions of this that could do different layouts, and then later on I simply want to call this:
Container(
color:Colors.grey[400],
padding:EdgeInsets.all(40),
child: Text("Please select cards",
style:TextStyle(
fontFamily:'Amatic',
fontSize:30,
fontWeight:FontWeight.bold
),
),
),
outputType1,
I get an error regarding type setting but I can't work out if I'm close to fixing it or if I the above code fundamentally does not work.
Thanks in advance...
You can only have type Widget in your build function. Change the type of outputType1 to Widget and you'll be fine.
Widget testFunction(){
return Container(
child: Text(
"Hello World"
),
);
}
Widget Function() outputType1 = testFunction;
and call the function in your build function
Container(
color:Colors.grey[400],
padding:EdgeInsets.all(40),
child: Text("Please select cards",
style:TextStyle(
fontFamily:'Amatic',
fontSize:30,
fontWeight:FontWeight.bold
),
),
),
outputType1(),
HI i want to ask about how to set layout for streambuilder and a container.
I have a streambuilder named streamdb,
and bellow the streambuilder i want to add, a button name buttonscan.
Container container = new Container(margin: const EdgeInsets.all(10.0),
child: new Row(margin: const EdgeInsets.all(10.0),
children: [streamdb,buttonscan]));
have tried :
children : <Widget>[streamdb],buttonname]
children : <Widget>[streamdb,buttonname]
children : [new Expanded(streamdb),buttonname]
When I compiled above code produce an error, how to build a streambuilder and a button ?
last but not least why is it so hard to create a post asking a problem in stackoverflow ?
just solve my own problem.
Hopefully it can help other people with same problems.
its better to add New Expanded() to the list builder , so it tell the apps to expand as much as necessary
sometimes the class could not resolve "this" method , so since dart-flutter can ignore space,its better for me to just do this :
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text('MY App TITLE'),
),
body: new Center(
child: new Column(
children: <Widget>[
new Expanded(child:
new StreamBuilder()),mybutton,]))
PS : Kindly post more comment for better codes. Thanks!!