I am working with the Screenshot package to capture an image of a LinearPercentIndicator widget. However, I keep getting the error when the function is called
NoSuchMethodError: The getter 'width' was called on null.
Receiver: null
Tried calling: width
I am not sure where this error is coming from. Any suggestions? The function is below
Future<String> progressBarPath(double progress) async {
final progressFile = File('${(await getTemporaryDirectory()).path}/image.png');
var indicator = LinearPercentIndicator(
percent: progress > 1.0 ? 1.0 : progress,
width: 100,
lineHeight: 13,
animation: true,
animationDuration: 1000,
progressColor: FlutterFlowTheme.primaryColor,
backgroundColor: Color(0xFFF1F4F8),
center: Text(
formatNumber(
progress > 1.0 ? 1.0 : progress,
formatType: FormatType.percent),
style: FlutterFlowTheme.bodyText1.override(
fontFamily: 'Lato',
color: Colors.black,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
barRadius: Radius.circular(20),
);
//this is where the conversion to an image should occur
screenshotController.captureFromWidget(
InheritedTheme.captureAll(context, Material(child: indicator)),
delay: Duration(seconds: 1),
).then((capturedProgress) async {
await progressFile.create(recursive: true);
await progressFile.writeAsBytes(capturedProgress);
});
print(progressFile.path);
return progressFile.path;
}
Related
I am implementing a SFSlider in my app. When I try to give a theme to the slider using SfTheme, I get an error:
RenderBox was not laid out: _RenderSlider#850b3 relayoutBoundary=up7 'package:flutter/src/rendering/box.dart': Failed assertion: line 2009 pos 12: 'hasSize'
My code is :
Container(
height: constraints.maxHeight*0.1,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: SfTheme(
data: SfThemeData(
sliderThemeData: SfSliderThemeData(
activeLabelStyle: TextStyle(color: Colors.white),
inactiveLabelStyle: TextStyle(color: Colors.white),
)
),
child: SfSlider(activeColor: Colors.green,
inactiveColor: Colors.grey,
min: 0.0,
max: 100.0,
value: _value,
interval: 25,
showTicks: true,
showLabels: true,
enableTooltip: true,
minorTicksPerInterval: 1,
onChanged: (dynamic value) async {
totalAmount = await calculateData();
totalAmount = totalAmount.ceil() + 0.0;
setState(() {
_value = value;
total_amount_display = totalAmount;
});
},
),),),),
The Container is inside a column, which in turn is inside a container in dialog box.
When I remove the theme, the slider is rendered perfectly.
You can overcome this issue by setting color and fontSize when setting the text style for the active and inactive labels in SfSliderThemeData.
SfTheme(
data: SfThemeData(
sliderThemeData: SfSliderThemeData(
activeLabelStyle: const TextStyle(color: Colors.red, fontSize: 14),
inactiveLabelStyle: const TextStyle(color: Colors.red, fontSize: 14),
)
),
)
If you like to set the color alone for the label,then you can use the textTheme’s text style values retrieved from context’s ThemeData and using the copyWith() method to set the desired color.
final ThemeData themeData = Theme.of(context);
SfTheme(
data: SfThemeData(
sliderThemeData: SfSliderThemeData(
activeLabelStyle: themeData.textTheme.bodyText1!.copyWith(color: Colors.red),
inactiveLabelStyle: themeData.textTheme.bodyText1!.copyWith(color: Colors.red),
)
),
)
Sample: https://www.syncfusion.com/downloads/support/directtrac/general/ze/i4007071991344175
I have a slider that uses a value to render himself, it start rendering after I hit a button but the first time I render it, it gives me an error and then starts working properly.
This is the error:
The getter 'inSeconds' was called on null.
Receiver: null
Tried calling: inSeconds
This is the widget that uses the data:
child: Consumer<MyAudio>(
builder: (context, audioPlayer, child) =>
SleekCircularSlider(
appearance: CircularSliderAppearance(
customWidths: CustomSliderWidths(
progressBarWidth: 2.0,
trackWidth: 1.0,
handlerSize: 1.0,
shadowWidth: 1.0,
),
infoProperties: InfoProperties(
modifier: (value) => '',
),
customColors: CustomSliderColors(
trackColor: Colors.grey,
progressBarColor: Colors.black,
),
size: 4.0,
angleRange: 360,
startAngle: -90.0,
),
min: 0.0,
max: audioPlayer.songLength.inSeconds.toDouble(),
initialValue:
audioPlayer.position.inSeconds.toDouble(),
),
),
),
And this is the function that gives me the values songLength and position:
class MyAudio extends ChangeNotifier {
Duration songLength;
Duration position;
AudioPlayer _player = AudioPlayer();
AudioCache cache;
MyAudio() {
initAudio();
}
initAudio() {
cache = AudioCache(fixedPlayer: _player);
_player.onDurationChanged.listen((Duration d) {
songLength = d;
notifyListeners();
});
_player.onAudioPositionChanged.listen((Duration p) {
position = p;
notifyListeners();
});
I think I should use an async function, what do you think?
If you need more code here's my github repo with all the files: https://github.com/astroxd/sputofy_mobile/tree/main/sputofy_2
the slider is in /lib/miniPlayer and the values are in /lib/model/audioPlayer
The problem is that when the widget is built for first time, the audioPlayer.songLength and audioPlayer.position are null despite audioPlayer is non-null.
In this fragment of code you define a pair of listeners, but the callbacks are called after the first build.
_player.onDurationChanged.listen((Duration d) {
songLength = d;
notifyListeners();
});
_player.onAudioPositionChanged.listen((Duration p) {
position = p;
notifyListeners();
});
Then, a solution can be the following:
child: Consumer<MyAudio>(
builder: (context, audioPlayer, child) => SleekCircularSlider(
appearance: CircularSliderAppearance(
customWidths: CustomSliderWidths(
progressBarWidth: 2.0,
trackWidth: 1.0,
handlerSize: 1.0,
shadowWidth: 1.0,
),
infoProperties: InfoProperties(
modifier: (value) => '',
),
customColors: CustomSliderColors(
trackColor: Colors.grey,
progressBarColor: Colors.black,
),
size: 4.0,
angleRange: 360,
startAngle: -90.0,
),
min: 0.0,
max: audioPlayer.songLength?.inSeconds?.toDouble() ?? 0.0,
initialValue: audioPlayer.position?.inSeconds?.toDouble() ?? 0.0,
),
)
Or maybe use a loader instead.
want to get an animated container small again to initial size with a click. It expands perfectly but I can't close it again.............................................................................................................................
child: AnimatedContainer(
duration: Duration(milliseconds: 200),
width: isTextOpen ? 200 : 95,
height: 50,
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(3)),
border: isTextOpen
? Border.all(
color: Colors.deepOrangeAccent,
width: 1.0,
)
: null,
),
child: isTextOpen
? TextField()
: GestureDetector(
onTap: () {
setState(() {
isTextOpen = true;
kapaac = 1;
});
},
child: Text(
"User_007",
style: TextStyle(
color: Colors.deepOrangeAccent,
fontSize: 16.0,
fontWeight: FontWeight.bold,
),
),
),
),
You are not setting it back to false.
setState(() => isTextOpen = !isTextOpen);
Once the user clicks the textbox if you want to get the textBox size to the initial size then you need to define a double variable with the expanded size then once the user clicks the text box for entering the text you need to reset the variable
double animatedContainerSize = 200;
After that set your AnimatedContainer widget width with the variable
width: isTextOpen ? animatedContainerSize : 95,
Then in your text box onTap function change the size to initial size with setState
isTextOpen
? TextField(
onTap: () {
setState(() {
animatedContainerSize = 95;
});
},
)
I am using progress_dialog 1.2.0 package to show a progress dialog in my app, it is showing when I call pr.show() but not getting hidden when I call pr.hide():
onTap: () async {
pr.show();
print('clicked custom category');
print(categorylist[index].catName);
print(categorylist[index].catId);
// await getAllProductsInCategory(categorylist[index].catId);
setState(() {
catId = categorylist[index].catId;
myinitlist.clear();
myinitlist = List.from(productList);
pr.hide();
});
},
When I uncomment that getAllProductsInCategory() function it hides the dialog.
Try with :
onTap: () async {
pr.show();
print('clicked custom category');
print(categorylist[index].catName);
print(categorylist[index].catId);
setState(() {
catId = categorylist[index].catId;
myinitlist.clear();
myinitlist = List.from(productList);
Future.delayed(Duration(seconds: 3)).then((value) {
pr.hide().whenComplete(() {
print(pr.isShowing());
});
});
});
},
or :
onTap: () async {
pr.show();
print('clicked custom category');
print(categorylist[index].catName);
print(categorylist[index].catId);
Future.delayed(Duration(seconds: 3)).then((value) {
setState(() {
catId = categorylist[index].catId;
myinitlist.clear();
myinitlist = List.from(productList);
pr.hide().whenComplete(() {
print(pr.isShowing());
});
});
});
},
Please use await keyword when you are using async calls to start progressDialog & hide:
await progressDialog.show();
await progressDialog.hide();
Example:
Add the Package:
dependencies:
progress_dialog: ^1.2.4
import 'package:progress_dialog/progress_dialog.dart';
Create and initialize a ProgressDialog object inside the build() method passing context to it.
Initialize the ProgressDialog object:
final ProgressDialog pr = ProgressDialog(context);
By default it is a normal dialog to show some message, if you would like to use it to show percentage of progress done, specify the optional type parameter and specify if you want your dialog to dismiss when back button is pressed isDismissible parameter (Optional):
//For normal dialog
pr = ProgressDialog(context,type: ProgressDialogType.Normal, isDismissible: true/false, showLogs: true/false);
//For showing progress percentage
pr = ProgressDialog(context,type: ProgressDialogType.Download, isDismissible: true/false, showLogs: true/false);
> Note: Please initialize the ```ProgressDialog```, where you have availability of the context
Style the progress dialog (Optional)
pr.style(
message: 'Downloading file...',
borderRadius: 10.0,
backgroundColor: Colors.white,
progressWidget: CircularProgressIndicator(),
elevation: 10.0,
insetAnimCurve: Curves.easeInOut,
progress: 0.0,
textDirection: TextDirection.rtl,
maxProgress: 100.0,
progressTextStyle: TextStyle(
color: Colors.black, fontSize: 13.0, fontWeight: FontWeight.w400),
messageTextStyle: TextStyle(
color: Colors.black, fontSize: 19.0, fontWeight: FontWeight.w600)
);
Note: You don't need to use all parameters, all of them are optional.
Showing the progress dialog:
await pr.show();
Dynamically update the content shown out there
pr.update(
progress: 50.0,
message: "Please wait...",
progressWidget: Container(
padding: EdgeInsets.all(8.0), child: CircularProgressIndicator()),
maxProgress: 100.0,
progressTextStyle: TextStyle(
color: Colors.black, fontSize: 13.0, fontWeight: FontWeight.w400),
messageTextStyle: TextStyle(
color: Colors.black, fontSize: 19.0, fontWeight: FontWeight.w600),
);
Dismissing the progress dialog:
pr.hide().then((isHidden) {
print(isHidden);
});
// or
await pr.hide();
Navigating to next screens must be done after the completion of Future - hide(). See here for example.
Check if progress dialog is showing:
bool isProgressDialogShowing = pr.isShowing();
print(isProgressDialogShowing);
Use custom body
pr = ProgressDialog(
context,
type: ProgressDialogType.Normal,
isDismissible: true,
/// your body here
customBody: LinearProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.blueAccent),
backgroundColor: Colors.white,
),
);
For more details: https://flutterrepos.com/repo/fayaz07-progress_dialog-
I have 10 field on a form and I want have a status tracker similar to the image below..
Basically , I will pass the percent and the widget should be updated
So if 4 items are completed then it will be 40% complete,,
Can you give me some hints on how to create this widget
I think this is somehow weird but may help!
you need a validator
You could make a function called checkProgress() and assign it to the onChanged of the textformfield:
void checkProgress() {
if(textformfieldcontroller.isValid) {
setstate(() {
progressbar = progressbar + 10;
});
} else if (textformfieldcontroller.isNotValid) {
setstate(() {});
}
}
It may work, but if not the concept is using if statements with setstate.
You can use the library percent_indicator
LinearPercentIndicator(animation: true,
lineHeight: 15,
animationDuration: 5000,
percent: 0.4,
backgroundColor: Colors.blue,
progressColor: Colors.orange,
center: Text(
'Text',
style: TextStyle(
color: Theme.of(context).primaryColorDark,
fontSize: 15,
fontWeight: FontWeight.w500),
),
linearStrokeCap: LinearStrokeCap.roundAll,
),
the parameter "percent" shows the progress in the indicator