I am using a Flutter slider widget, where tapping/dragging on the slider moves the progress/activeColor of the slider. However, it seems like only directly touching the slider causes an event to happen, and it's hard to always touch a finger directly onto the slider. Is there a way to expand the Slider's "touch zone"?
This is what I have:
return new Center(
child: new Container(
height: 2.0,
child: new Slider(
min: 0.0,
max: 1.0,
activeColor: Colors.grey[50],
value: _getUnitProgress(model),
onChanged: (double value) => _unitSeek(value, model),
),
),
);
I faced a very similar issue recently and found that it was too easy a problem!
The flutter slider which you are using is in itself a renderBox which detects gesture all over it's given area (it uses a GestureArena), the only thing you have to do is too increase the tap area is that you give the widget more area, one of the easiest way to do that is that wrap the slider in a container and give the container enough height and width!
return Container(
height: 100,
child: Slider(
value: _value.toDouble(),
min: 1,
max: 10,
divisions: 10,
label: _value.toString(),
onChanged: (double newValue) {
setState(() {
_value = newValue.round();
},
);
},
),
);
In this example the container height is 100 thus the tap area in this case will be 50 above the slider and 50 below, the slider will be exactly in the middle.
Hope it helps!
You don't want to wrap your Slider in a Container with a height. The Slider has a _kReactionRadius that expands the touch zone for a user. This means that a user doesn’t have to touch directly onto the Slider’s horizontal line to trigger the onTap():
return Center(
child: new Slider(
min: 0.0,
max: 1.0,
activeColor: Colors.grey[50],
value: _getUnitProgress(model),
onChanged: (double value) => _unitSeek(value, model),
),
);
The easy way is to get the actual SliderTheme used in your context and modify only the properties you need. For example, to modify one slide:
SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: Colors.white,
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 15.0),
overlayShape: RoundSliderOverlayShape(overlayRadius: 30.0),
),
child: Slider(
value: height.toDouble(),
min: 120.0,
max: 220.0,
activeColor: Colors.white,
inactiveColor: Color(0xFF8D8E98),
onChanged: (double newValue) {
setState(() {
height = newValue.round();
});
},
),
),
Another option is modify the theme you're using in your app; in this way you modify all the sliders in the app:
MaterialApp(
theme: ThemeData.dark().copyWith(
sliderTheme: SliderTheme.of(context).copyWith( //slider modifications
thumbColor: Color(0xFFEB1555),
inactiveTrackColor: Color(0xFF8D8E98),
activeTrackColor: Colors.white,
overlayColor: Color(0x99EB1555),
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 15.0),
overlayShape: RoundSliderOverlayShape(overlayRadius: 30.0),
),
primaryColor: Color(0xFF0A0E21), // theme color
scaffoldBackgroundColor: Color(0xFF0A0E21)), // theme background color
home: InputPage(),
);
Related
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!!
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