How to use Pageable lazy list - dart

I want to create a very long list of widgets that is user might want to scroll through, like a book.
I noticed PageableLazyList class, but couldn't quite understand how should I use it.
class NotePage extends PageableLazyList {
NotePage({Key key, this.note}) : super(key: key,itemBuilder: itembuilder);
final Note note;
static List<NoteContainer> itembuilder (BuildContext context, num start, num count ){
List<NoteContainer> result = <NoteContainer>[];
for(num i = start; i < start+count; i++){
result.add(new NoteContainer(new Note(title: "Note " + i.toString(), description: "Description " + i.toString())));
}
return result;
}
}
didn't work! I need a build method here, but couldn't understand what I am supposed to do.

Well this actually works:
class NotePageList extends PageableLazyList {
NotePageList({Key key}) : super(key: key,
scrollDirection: Axis.horizontal,
itemCount : 10000,
itemBuilder: itembuilder){
}
static List<NoteContainer> itembuilder (BuildContext context, num start, num count ){
List<NoteContainer> result = <NoteContainer>[];
for(num i = start; i < start+count; i++){
result.add(new NoteContainer(new Note(title: "Note " + i.toString(), description: "Description " + i.toString())));
}
return result;
}
}

Related

how to refresh the display of a DragTarget Widget

Initially my 'TRY' pockets are empty and display thus:
Empty pockets
The pockets are then filled by the user dragging counters into them. Once all the pockets are filled e.g.
Filled pockets
they are then processed and the pockets can then be emptied and should be redisplayed. And that is the problem: the counters are still showing.
Here is the code for the DragTarget:
/// Drag Target --------
class CatcherPocket extends StatefulWidget {
final int position;
int colourID;
Color colour;
CatcherPocket(
{Key? key,
required this.position,
required this.colourID,
required this.colour})
: super(key: key);
#override
State<CatcherPocket> createState() {
// ignore: no_logic_in_create_state
return CatcherPocketState(
position: position, colourID: colourID, colour: colour);
}
}
class CatcherPocketState extends State<CatcherPocket> {
final int position;
int colourID;
Color colour;
CatcherPocketState(
{required this.position, required this.colourID, required this.colour});
#override
DragTarget build(BuildContext context) {
return DragTarget<CounterItem>(
builder: (context, candidateItems, rejectedItems) {
return Pocket(colourID: colourID, colour: colour);
},
onAccept: (ci) {
setState(() {
colourID = ci.uid;
colour = ci.imageProvider.colour;
});
GameState.updateTry(position, colourID, colour);
},
);
}
}
// ---------------- END OF Draggable counter stuff ------------------
I populate a list of pocket handles when they are initially built and use these to reset their internal state using the following code:
static List<CatcherPocket> cps = []; // to be populated on initilization
static resetTryCounters() {
for (CatcherPocket cp in cps) {
cp.colour = DDDtheme.standard.primaryColor;
cp.colourID = -1;
}
}
After invoking a 'setState' in the root ancestor Widget I would expect it to redraw the pockets in this reset state but it doesn't.
Any ideas why and a fix would be greatly appreciated
After some days of not thinking about it, the solution came to me. But it is crude and I am sure a more elegant solution is possible. Basically it involves changing the state of the CatcherPockets externally.
I modified the CatcherPocket to keep a handle (the 'cps' variable) to its state:
class CatcherPocket extends StatefulWidget {
final int position;
int colourID;
Color colour;
late CatcherPocketState cps = CatcherPocketState(
position: position, colourID: colourID, colour: colour);
CatcherPocket(
{Key? key,
required this.position,
required this.colourID,
required this.colour})
: super(key: key);
#override
State<CatcherPocket> createState() {
// ignore: no_logic_in_create_state
return cps;
}
}
This enabled a modified 'resetTryCounters' to get at the CatcherPocketState thus:
static List<CatcherPocket> cpockets = [];
static resetTryCounters() {
for (CatcherPocket cp in cpockets) {
cp.colour = DDDtheme.standard.primaryColor;
cp.colourID = -1;
if (cp.cps.mounted) {
cp.cps.colour = DDDtheme.standard.primaryColor;
cp.cps.colourID = -1;
cp.cps.setState(() {});
}
}
}
It works, but it blows the idea of encapsulation out of the water. Also, I wonder if there is a better way of passing the initialization parms for CatcherPocket to its State.

Expected a value of type 'Widget', but got one of type 'Null'

i am learning responsive web but i am stuck here in some kind of error like
Expected a value of type 'Widget', but got one of type 'Null'
don't know how to fix this error i tried a lot but i think i can't solve this without someone's help.
import 'package:flutter/material.dart';
const int largeScreenSize = 1366;
const int mediumScreenSize = 768;
const int smallScreenSize = 360;
const int customScreenSize = 1100;
class Responsiveness extends StatelessWidget {
final Widget? largeScreen;
final Widget? mediumScreen;
final Widget? smallScreen;
const Responsiveness({
this.largeScreen,
this.mediumScreen,
this.smallScreen,
});
#override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
double _width = constraints.maxWidth;
if (_width >= largeScreenSize) {
return largeScreen as Widget;
} else if (_width >= mediumScreenSize && _width < largeScreenSize) {
return mediumScreen ?? largeScreen as Widget;
} else {
return smallScreen ?? largeScreen as Widget;
}
});
}
}
largeScreen, mediumScreen and smallScreen all have the type Widget?, meaning: "either a Widget or null.
When you return largeScreen as Widget, if largeScreen is null, you will get an error, because null is not a valid value for Widget (just like 123 as String would throw, since the types are not assignable).
Looking at your code, if all 3 of those variables are null, you'd eventually try to return null from the LayoutBuilder parameter, which is always an error, because it returns a non-null Widget.
Make sure you account for the case where all of these are null, or make sure they are never all null at the same time (perhaps with an assert statement).

How to wait for the future object before proceeding in flutter?

I am writing a code in flutter in which I am using an SQFlite database. I want to insert image widget from the assets, and I am getting the name of the image from database.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Single Line diagram"),backgroundColor: Colors.red.shade700,),
body: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Align(
//alignment: Alignment.center,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Row(
//crossAxisAlignment: CrossAxisAlignment.center,
children: imageList(),
),
),
)
),
);
}
The above code calls imageList() for the list of images to display.
List<Widget> imageList(){
List<Widget> singleLineImages = new List();
List unit;
for (int i = 0; i <= widget.unitsList.length-1; i++){
for (int j = 1; j <= int.parse(widget.unitsList[i].quantity); j++){
print("${widget.unitsList[i].bulletin}, ${widget.unitsList[i].mountType}, ${widget.unitsList[i].disconnect}");
getfileName(widget.unitsList[i].bulletin, widget.unitsList[i].mountType, widget.unitsList[i].disconnect);
//if(fileName != null) {
singleLineImages.add(
Image.asset("images/SD_Files_2100/$fileName.jpg", height: 400.0, width: 200.0,));
//}
}
}
return singleLineImages;
}
I am getting the filename from getFileName() method which is using the database.
getfileName(String bulletin, String mountType, String disconnect)async {
fileNameList = await db.getSDfileName(bulletin, disconnect, mountType);
fileName = fileNameList[0]['FileName'];
print("filename: $fileName");
}
Now, after calling the getFileName(), the program is not waiting for the fileName and proceeding further, which takes filename as null. The filename is obtained correctly after the Image.asset code. Is there any way, so that the program waits untill it gets the proper filename?
Start fetching the list in initState() and call setState when the list is fetched, to do this asynchronously. Below you can find a simplified example of this process. Also note the await statement before get Filename. This makes sure you return to that piece of code after is is done executing.
class ListPage extends StatefulWidget {
#override
_ListPageState createState() => _ListPageState();
}
class _ListPageState extends State<ListPage> {
// This should actually be a List<MyClass> instead of widgets.
List<Widget> _list;
#override
void initState() {
super.initState();
_fetchList();
}
Future _fetchList() async {
List<Widget> singleLineImages = new List();
List unit;
for (int i = 0; i <= widget.unitsList.length-1; i++){
for (int j = 1; j <= int.parse(widget.unitsList[i].quantity); j++){
print("${widget.unitsList[i].bulletin}, ${widget.unitsList[i].mountType}, ${widget.unitsList[i].disconnect}");
String fileName = await getfileName(widget.unitsList[i].bulletin, widget.unitsList[i].mountType, widget.unitsList[i].disconnect);
singleLineImages.add(
Image.asset("images/SD_Files_2100/$fileName.jpg", height: 400.0, width: 200.0,));
}
}
// call setState here to set the actual list of items and rebuild the widget.
setState(() {
_list = singleLineImages;
});
}
#override
Widget build(BuildContext context) {
// Build the list, or for example a CircularProcessIndicator if it is null.
}
}
Sidenote: you are making a lot of calls to the database, which is probably inefficient. Try to get the required data in a single db call. But that is another topic.
Keep Future<Widget> _list; field in your class.
Add _list = _fetchList(); in the initState() function.
Also note that _fetchList should return Future<Widget> in this case.
Use FutureBuilder in your build function.

passing string as FontAwesomeIcons

am using font_awesome_flutter in my app to display some icons..
am getting the name of the icons from json as strings .. how i can pass it to the icon?
there is a way to achieve this?
for example:
am getting from the json:
String icon = 'ad';
and then i want to use it like this:
new Icon(FontAwesomeIcons.icon),
i know it doesn't work like this .. but how can i do this? is it doable?
I found one way may that help you. edit font_awesome_flutter.dart file as following and also access as below.
I just demonstrate with two Icon you can go on with as much as you need or for all.
font_awesome_flutter.dart
library font_awesome_flutter;
import 'package:flutter/widgets.dart';
import 'package:font_awesome_flutter/icon_data.dart';
// THIS FILE IS AUTOMATICALLY GENERATED!
class FontAwesomeIcons {
static const createDoc = {
'fiveHundredPx': IconDataBrands(0xf26e),
'accessibleIcon': IconDataBrands(0xf368),
//.......add all Icons HERE
};
static const IconData fiveHundredPx = const IconDataBrands(0xf26e);
static const IconData accessibleIcon = const IconDataBrands(0xf368);
static const IconData accusoft = const IconDataBrands(0xf369);
static const IconData acquisitionsIncorporated = const IconDataBrands(0xf6af);
static const IconData ad = const IconDataSolid(0xf641);
static const IconData addressBook = const IconDataRegular(0xf2b9);
static const IconData solidAddressBook = const IconDataSolid(0xf2b9);
static const IconData addressCard = const IconDataRegular(0xf2bb);
static const IconData solidAddressCard = const IconDataSolid(0xf2bb);
//.......
//.......add all Icons HERE To as already in your file
}
Now you can use as Following Code:
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
void main() {
runApp(new FontAwesomeGalleryApp());
}
class FontAwesomeGalleryApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Font Awesome Flutter Gallery',
theme: new ThemeData.light().copyWith(
iconTheme: new IconThemeData(size: 36.0, color: Colors.black87),
textTheme: new TextTheme(
body1: new TextStyle(fontSize: 16.0, color: Colors.black87),
),
),
home: new Home(),
);
}
}
class Home extends StatelessWidget {
String data = 'fiveHundredPx';
#override
Widget build(BuildContext context) {
return Scaffold(
body: new Container(
child: Center(
child: Icon(FontAwesomeIcons.createDoc[data.toString()]),
),
),
);
}
}
I know That This Way is little bit tough but i found this way only.
You can save the unicode data in the Json. For example :
static const IconData clock = const IconDataRegular(0xf017);
save f017 in the Json.
Later use the following function for conversion into an int.
int getHexFromStr(String fontCode) {
int val = 0;
int len = fontCode.length;
for (int i = 0; i < len; i++) {
int hexDigit = fontCode.codeUnitAt(i);
if (hexDigit >= 48 && hexDigit <= 57) {
val += (hexDigit - 48) * (1 << (4 * (len - 1 - i)));
} else if (hexDigit >= 65 && hexDigit <= 70) {
// A..F
val += (hexDigit - 55) * (1 << (4 * (len - 1 - i)));
} else if (hexDigit >= 97 && hexDigit <= 102) {
// a..f
val += (hexDigit - 87) * (1 << (4 * (len - 1 - i)));
} else {
throw new FormatException("An error occurred when converting");
}
}
return val;
}
And finally use the following :
Icon(IconDataSolid(getHexFromStr(' f017')))

Flutter: Using NumberFormat in TextInputFormatter

I'm trying to use NumberFromatter in TextInputFormatter but when I try to use it, it completely messed up! This is my TextInputFormatter implementation code:
class NumericTextFormatter extends TextInputFormatter {
TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
if(newValue.text.length > 0) {
int num = int.parse(newValue.text.replaceAll(',', ''));
final f = new NumberFormat("#,###");
return newValue.copyWith(text: f.format(num));
} else {
return newValue.copyWith(text: '');
}
}
}
So when I add this formatter to a TextField and try to type 1 to 9, what I expect to see is something like: 123,456,789
But this is what shows in TextField:
1
12
123
1,234
12,354 <- this is where it starts
123,564
1,235,674
12,356,874 <- and it happends again
It seems that cursor moves after adding one , character. So can anyone helps me with this?
This is because after you format the value you are adding a new char but the text selection remains at the same position, one char less, this cause an expected behavior
You can modify your TextInputFormatter like this:
Fixed to support all locales and to remember cursor position
class NumericTextFormatter extends TextInputFormatter {
#override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
if (newValue.text.isEmpty) {
return newValue.copyWith(text: '');
} else if (newValue.text.compareTo(oldValue.text) != 0) {
final int selectionIndexFromTheRight =
newValue.text.length - newValue.selection.end;
final f = NumberFormat("#,###");
final number =
int.parse(newValue.text.replaceAll(f.symbols.GROUP_SEP, ''));
final newString = f.format(number);
return TextEditingValue(
text: newString,
selection: TextSelection.collapsed(
offset: newString.length - selectionIndexFromTheRight),
);
} else {
return newValue;
}
}
}
Based on the answer and for people from Europe looking for a quick fix
class NumericTextFormatter extends TextInputFormatter {
#override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
final currencySymbol = '€';
if (newValue.text.isEmpty || newValue.text.trim() == currencySymbol) {
return newValue.copyWith(text: '');
} else if (newValue.text.compareTo(oldValue.text) != 0) {
var selectionIndexFromTheRight =
newValue.text.length - newValue.selection.end;
final f =
NumberFormat.currency(locale: 'de', decimalDigits: 0, symbol: '');
var num = int.parse(newValue.text.replaceAll(RegExp('[^0-9]'), ''));
final newString = '$currencySymbol ' + f.format(num).trim();
return TextEditingValue(
text: newString,
selection: TextSelection.collapsed(
offset: newString.length - selectionIndexFromTheRight),
);
} else {
return newValue;
}
}
}

Resources