Generate widgets in-between texts in flutter - flutter

I am trying to generate a list of pages numbers after every 300 words but can't. please does anybody know how I can implement this?.
class Try extends StatelessWidget {
const Try({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final String text4 = lorem(paragraphs: 8, words: 2000);
return Scaffold(
body: SingleChildScrollView(
child: SafeArea(
child: Padding(
padding: const EdgeInsets.all(9),
child: Column(
children: [
Text(text4),
],
),
),
),
),
);
}
}
I tried adding the below code to it but...
for (int i = 0; i < text4.length;300 * i++)
//if (text4.length == 300)
Padding(
padding: const EdgeInsets.all(14),
child: Column(
children: [
const Text('page ${a += 1}'),
Divider(
color: Theme.of(context).primaryColor,
)
],
),
),
Text(text4),

You can use substring to extract text and page number logic will be i / 300 + 1. It will provide 300 letters. For word case you need to convert text to list by splitting on space.
final wordList = text4.split(" ").toList();
String text4 =
"I am trying to generate a list of pages numbers after every 300 words but can't. please does anybody know how I can implement this?.I am trying to generate a list of pages numbers after every 300 words but can't. please does anybody know how I can implement this?.I am trying to generate a list of pages numbers after every 300 words but can't. please does anybody know how I can implement this?.I am trying to generate a list of pages numbers after every 300 words but can't. please does anybody know how I can implement this?.I am trying to generate a list of pages numbers after every 300 words but can't. please does anybody know how I can implement this?.I am trying to generate a list of pages numbers after every 300 words but can't. please does anybody know how I can implement this?.";
late final wordList = text4.split(" ").toList();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
padding: EdgeInsets.all(16.0),
child: Column(
children: [
// for (int i = 0; i < text4.length; i += 300)
for (int i = 0; i < wordList.length; i += 300)
Padding(
padding: const EdgeInsets.all(14),
child: Column(
children: [
Text('page ${i / 300 + 1}'),
Divider(
color: Theme.of(context).primaryColor,
),
/// for letters
// Text(text4.substring(
// i,
// i + 300 > text4.length ? text4.length : i + 300,
// )),
() {
final textX = wordList
.sublist(
i,
i + 300 > wordList.length
? wordList.length
: i + 300)
.toString();
return Text(textX.substring(1, textX.length - 1));
}()
],
),
),
],
),
),
);
}
[] depends on original text, for substring it is handled Text(textX.substring(1, textX.length - 1));.

Related

Create Dynamically Sized Squares

I'm attempting to create a GitHub style heat map inside a Card and am struggling with the UI. The challenge is making the heat map dynamically expand to fit the Card it sits in based on the device's screen size.
Here is an example screenshot.
The code to create the screenshot is below.
Essentially the code,
creates a column that starts with two lines of text
then inserts a Row of Columns that consist of squares
I'm not sure if I should focus on making the individual boxes expand, the columns that the individual boxes sit in, or both. All my experiments end in unbound errors. I'm not sure where/how to add the constraints.
I also assume I'll need the boxes to be wrapped in AspectRatio() to keep the 1:1 ratio and be a square.
(I've removed some of the the more verbose business logic in my actual code for simplicity.)
class ProfileView extends StatelessWidget {
const ProfileView({Key? key}) : super(key: key);
List<Widget> _heatMapColumnList() {
final _columns = <Widget>[];
final _startDate = DateTime.now().subtract(const Duration(days: 365));
final _endDate = DateTime.now();
final _dateDifference = _endDate.difference(_startDate).inDays;
for (var index = 0 - (_startDate.weekday % 7);
index <= _endDate.difference(_startDate).inDays;
index += 7) {
//helper to change date by index
final _firstDay = DateUtility.changeDay(_startDate, index);
_columns.add(
HeatMapColumn(
startDate: _firstDay,
endDate: index <= _dateDifference - 7
? DateUtility.changeDay(_startDate, index + 6)
: _endDate,
numDays: min(_endDate.difference(_firstDay).inDays + 1, 7),
),
);
}
return _columns;
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Card(
elevation: 1,
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text('Some Title Text'),
const Text('More SubTitle Text'),
const SizedBox(height: 10),
Row(
children: <Widget>[
..._heatMapColumnList(),
],
...
...
class HeatMapColumn extends StatelessWidget {
HeatMapColumn({
super.key,
required this.startDate,
required this.endDate,
required this.numDays,
}) : dayContainers = List.generate(
numDays,
(i) => HeatMapBox(
date: DateUtility.changeDay(startDate, 1),
),
),
emptySpace = (numDays != 7)
? List.generate(
7 - numDays,
(i) => const HeatMapBox(
date: null,
),
)
: [];
final List<Widget> dayContainers;
final List<Widget> emptySpace;
final DateTime startDate;
final DateTime endDate;
final int numDays;
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Column(
children: <Widget>[
...dayContainers,
...emptySpace,
],
...
// !!!THIS IS THE BOX I WANT TO DYNAMICALLY RESIZE!!!
class HeatMapBox extends StatelessWidget {
const HeatMapBox({
required this.date,
this.color,
super.key,
});
final DateTime? date;
final Color? color;
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(1),
child: SizedBox(
child: Container(
// ???HOW DO I AVOID THIS EXPLICIT NUMERIC CONTAINER SIZE???
height: 3,
width: 3,
decoration: const BoxDecoration(
color: Colors.black12,
),
),
),
);
}
}
I would add a comment but I do not have enough reputation so sorry if this is not the answer you are looking for
You could use something like this
double width = MediaQuery.of(context).size.width; // gives width of device screen
double height = MediaQuery.of(context).size.height; // gives height of device screen
// if the card has padding
double cardLeftPadding = a double;
double cardRightPadding = a double;
width -= (cardLeftPadding + cardRightPadding);
Container(
// ???HOW DO I AVOID THIS EXPLICIT NUMERIC CONTAINER SIZE???
height: 3,
width: width,
decoration: const BoxDecoration(
color: Colors.black12,
),),
I believe something like this will allow you to fit your heat map to the full length of your card

Flutter - Rows with Icons like a Bow

in my form I have a Container with some Rows and nested Icons. Every Icon has got a Tap event with a function.
How can I curve all Rows with Icons like this.
to obtain this result as a bow (like rainbow)?
I think there is no shortcut to do this if you want a smooth curve. You must provide some constrains that what the ratio of width and height and calculate the curve formulation. You can try to combine Stack & Align to fullfill your layout.
Here is my assumption from your image:
X axis is separated equally.
Y axis is like a circular sector which radian from 1.25 pi to 1.75 pi (a circle is 2 pi). So the total radian is pi
import 'dart:math';
class CustomRainbow extends StatelessWidget {
#override
Widget build(BuildContext context) {
List<Widget> icons = List<Icon>.filled(14, Icon(Icons.tag_faces));
return Scaffold(
backgroundColor: Colors.black87,
body: Center(
child: Container(
width: 300,
height: 130,
color: Colors.white,
child: Stack(
children: [
..._bridge(icons),
..._bridge(icons, row: 1),
..._bridge(icons, row: 2),
..._bridge(icons, row: 3),
],
),
),
),
);
}
List<Widget> _bridge(List<Widget> icons, {int row = 0}) {
double totalRad = pi;
double rowHeightFactor = 0.4;
int count = icons.length;
double width = 2.0 / (count - 1);
double singleRad = totalRad / (count + 1);
double startRad = pi * 1.5 - totalRad * 0.5;
List<Widget> result = [];
for (int i = 0; i < count; i++) {
result.add(
Align(
alignment: Alignment(
-1 + i * width, sin((startRad + singleRad * (i + 1)))) +
Alignment(0, row * rowHeightFactor),
child: icons[i],
),
);
}
return result;
}
}
You can still tune the Width, Height, totalRad and rowHeightFactor,
Make sure the Alignment value is in range of (-1.0, 1.0) and all the icons will be in the Container.
A hack could be to display row(column) and to add a transparent (default) container with the respective height for each column as the first element, to move the subsequent icons downwards.
One simple way of doing that is to build Columns inside the Row with placeholders that will ocuppy some bottom space to make taller columns:
static const double size = 40.0;
static const placeholderSize = size / 2;
static const int columnsNumber = 8;
Widget repeatedWidget() {
return IconButton(
icon: Icon( //Your icon button
Icons.tag_faces,
size: size,
),
onPressed: () {},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate( //generating a fixed number of columns with icons for the example
columnsNumber,
(index) {
return Column(
mainAxisAlignment: MainAxisAlignment.end,
children: List.generate(
6,
(index) => repeatedWidget(),
)..add( //adding a widget at the final of each Column
Column(
children: List.generate(
/*if the current index is greater or equal to half of the number
of columns return it minus index - 1, else return the index*/
index >= columnsNumber / 2
? columnsNumber - index - 1
: index,
(index) => Container( //containers that will ocuppy the bottom space of each column
height: placeholderSize,
),
),
),
),
);
},
),
),
);
}
The result:

RangeError (index): Invalid value: Valid value range is empty: 1

after whole day of trying to solve this myself I had to come and ask for help.
I'm trying to build this ListView.builder, it has fixed amount of itemCount. And its building Widgets using data retrieved from locally stored JSON file.
I'm using Provider to pass that data around. The problem is, on app start or hot restart that ListView.builder turns red and shows error, and then after like quarter of a second it shows my data.
I understand why this happens, my list of data that I get from json is initially empty. So I put ternary operator like: provider.data == null ? CircularProgressIndicator() : ListView.builder... but this doesnt stop it from crashing.
I dont know why and its driving me crazy. Here is full code:
We are talking here about widget called RecommendedCardList, its showing widgets from above mentioned list by having random number (in range of list length) as index.
I have similar ListView on HomeScreen called CategoryCardList and its working similarly to RecommendedCardList but I'm not having this issue with it. Also the rest of the home screen shows good, only the portion where RecommendedCardList is turns red for a short period of time.
Home Screen class:
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
// Get user's screen properties
// We are using this properties to adjust size accordingly
// In order to achieve maximum responsivnes for different screen sizes
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
var repoProvider = Provider.of<Repository>(context);
var recipeDataList = repoProvider.recipeDataList;
return Container(
color: backgroundColor,
child: repoProvider.recipeDataList == null
? Center(child: CircularProgressIndicator())
: Padding(
padding: contentPadding,
child: ListView(
children: <Widget>[
AppTitle(),
SizedBox(
height: height * 0.03,
),
Column(
children: <Widget>[
CategoryAndSeeAll(),
CategoryCardsList(height: height, provider: repoProvider),
],
),
SizedBox(
height: height * 0.05,
),
Container(
width: double.infinity,
height: height * 0.1,
decoration: BoxDecoration(
border: Border.all(color: accentColor),
),
child: Text(
'Reserved for AD',
textAlign: TextAlign.center,
),
),
SizedBox(
height: height * 0.05,
),
RecommendedCardsList(height: height, width: width, recipeDataList: recipeDataList),
],
),
),
);
}
}
RecommendedCardsList class:
class RecommendedCardsList extends StatelessWidget {
const RecommendedCardsList({
Key key,
#required this.height,
#required this.width,
#required this.recipeDataList,
}) : super(key: key);
final double height;
final double width;
final recipeDataList;
#override
Widget build(BuildContext context) {
return Container(
height: height * 0.30,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: numberOfRecommendedRecipes,
itemBuilder: (context, counter) {
int randomNumber = Random().nextInt(recipeDataList.length);
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
RecommendedCard(
width: width,
height: height,
imagePath: recipeDataList.elementAt(randomNumber).image,
text: recipeDataList.elementAt(randomNumber).title,
),
],
);
}),
);
}
}
Repository class:
class Repository extends ChangeNotifier {
Repository() {
loadJson();
}
var _recipeData;
List<RecipeModel> _recipeDataList = [];
List<RecipeModel> get recipeDataList => _recipeDataList;
void loadJson() async {
var json = await rootBundle.loadString('assets/recipes.json');
var parsedJson = jsonDecode(json);
for (var item in parsedJson) {
_recipeData = RecipeModel.fromJson(item);
_recipeDataList.add(_recipeData);
}
//print('Title:${_recipeDataList[0].title}\nImage:${_recipeDataList[0].image}'); // For debugging
notifyListeners();
}
}
This error is related to the fact that the code searched for an index in your list and this index is more than you list length.
I think the error is in that part:
int randomNumber = Random().nextInt(recipeDataList.length);
Supposing the length is 10 the random function will retrieve a num between 0 and 10, but the last index is 9.
With that in mind, I have two suggestions:
1)
// changing ternary logic
(repoProvider.recipeDataList == null && repoProvider.recipeDataList.length > 0)
2)
// inside ListView.Builder change to get the list length
itemCount: recipeDataList.length
Put the following condition in build() of RecommendedCardsList widget as the first line.
if(recipeDataList == null || recipeDataList.length == 0){
return Container();
}

how to fix too many variables in flutter

I'm trying to create stacks of cards in my Flutter project. Each card contains different data/information and when I try visualize with a dummy data, I have to use a lot of variables which is pretty much repeating variable name for each card. Is there aways to make a reusable card component in flutter so that I can make it clear and simple because when I use real data in the future, I might have more than 2 cards in a group and they will also have different data. Any suggestion will be really appreciated.
class MyConstructor {
MyConstructor({this.jonathan1,this.jonathan2,this.jonathan3});
}
class StackedCardsState extends State<HomePage> {
List<MyConstructor> cards = [
MyConstructor(h1: "Hello", h2: "hello3")
];
/////
Padding(
padding: EdgeInsets.all(15.0),
child: Column(children: [
Text(MyConstructor.hey, style: TextStyle(fontWeight: FontWeight.bold),),
Text(MyConstructor.hey),
Text(MyConstructor.hey, style: TextStyle(color: Colors.red[500]),),
VerticalDivider(color: Colors.blue),
])),
Your problem is first of all rather simple, you are violating the DRY concept (Don't repeat yourself, https://en.wikipedia.org/wiki/Don%27t_repeat_yourself ).
As soon as you start copy pasting code take a moment and think about your code and how you can abstract it into a reusable component.
Another big issue that I think you are lacking is variable naming. It is a very very important part of writing code. Might seem trivial but it will be very hard to understand what a variable named cardOne1 and cardTwo2 actually mean. What is the purpose of that variable? What does it do?
Now with that said I understand your app has something to do with car sales but other than that I'm not really sure what I'm looking at. There for I will have a harder time finding a good variable for this code but here is an example.
So lets break down the contents in the card to a single reusable widget, we can also make a data class (or model) for storing the data that we then give to the widget.
//car_details.dart
class CarDetails {
String title;
String diffNumber;
String diffPercent;
Color colorIndicator;
CarDetails({
this.title,
this.diffNumber,
this.diffPercent,
this.colorIndicator,
});
}
//car_card_details.dart
class CarCardDetails extends StatelessWidget {
final double padding;
final CarDetails carDetails;
CarCardDetails({
this.carDetails,
this.padding = 15,
});
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
carDetails.colorIndicator != null
? Container(
color: carDetails.colorIndicator,
height: 60,
width: 2,
)
: Container(),
Padding(
padding: EdgeInsets.all(padding),
child: Column(children: [
Text(carDetails.title),
Text(carDetails.diffNumber),
Text(carDetails.diffPercent),
VerticalDivider(color: Colors.blue),
])),
],
);
}
}
To use this component we make a CarCard Widget that takes a title and a list of CarDetails like so:
// car_card.dart
class CarCard extends StatelessWidget {
final String title;
final List<CarDetails> carDetails;
CarCard({this.title, this.carDetails});
#override
Widget build(BuildContext context) {
List<Widget> detailRow = List();
if (carDetails != null) {
carDetails.forEach((element) {
detailRow.add(CarCardDetails(
top: element.title,
middle: element.diffNumber,
bottom: element.diffPercent,
lineColor: element.colorIndicator,
));
});
}
return Container(
//height: 150, //I would not hardcode the height, let the childrent expand the widget instead
child: SingleChildScrollView(
child: Card(
elevation: 8.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
child: InkWell(
child: Column(children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(children: [
Text(
title,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
Spacer(),
Icon(Icons.favorite)
]),
),
Divider(color: Colors.black),
Row(children: detailRow),
]),
),
),
),
);
}
}
And instead of saving all the variables you had in app we can now make them into a list of CarDetails where each element contains the strings.
// some other widget
...
List<CarDetails> carDetails = [
CarDetails(
title: "2 hrs ago",
diffNumber: "+/ TRACK",
diffPercent: "% to DBJ",
),
CarDetails(
title: "CHEVEROLET",
diffNumber: "-2706",
diffPercent: "42.2%",
colorIndicator: Colors.red,
),
CarDetails(
title: "BUICK",
diffNumber: "+300",
diffPercent: "50%",
colorIndicator: Colors.green,
),
CarDetails(
title: "GMC",
diffNumber: "-712",
diffPercent: "52.1%",
colorIndicator: Colors.black26,
),
];
#override
Widget build(BuildContext context) {
return CarCard(
title: "US Daily Retail Delieveries by Brand",
carDetails: carDetails,
);
}
...
This can of course be abstracted even further with the groups of cards etc, etc. But I hope you get the idea.
This is an example of how you could do it, with that said I do not know what data you are intending to use and how you want to structure it. So consider this a starting point and take it from there. :)

Dynamically generate widgets in Flutter

I am trying to dynamically generate a set of widgets based on a particular condition. In this case I am trying to generate a list of RadioTiles
This is how I am trying to generate
List _listings = new List();
Widget _getListings() {
// TODO this will accept json objects in order to display the data
List listings = new List();
int i = 0;
for (i = 0; i < 5; i++) {
listings.add(
new RadioListTile<SingingCharacter>(
title: const Text('Lafayette'),
value: SingingCharacter.lafayette,
groupValue: _character,
onChanged: (SingingCharacter value) {
setState(() {
_character = value;
});
},
),
);
}
// return listings;
}
and I am trying to display this within a stateful widget like this :
return new SafeArea(
child: Column(children: <Widget>[
new Padding(
padding: const EdgeInsets.all(20.0),
child: new Text(
"Verify and Select a Single Listing?",
style: _textStyle,
),
),
ListView(
shrinkWrap: true,
padding: const EdgeInsets.all(20.0),
children: <Widget>[
_getListings(),
],
),
]));
The issue is that the value of listings is null due to which I am unable to display any widgets on the screen.
Any insights would be useful.
Thanks,
Edit :
If I do try to return a list this is what I see:
I am not sure if this is the best way to dynamically create widgets.
Here are some updates to your code:
Widget build(BuildContext context) {
return Scaffold(body: SafeArea(
child: Container(child: Column(children: <Widget>[
Padding(
padding: const EdgeInsets.all(20.0),
child: Text("Verify and Select a Single Listing?",),
),
Expanded(child: ListView(
padding: const EdgeInsets.all(20.0),
children: _getListings(), // <<<<< Note this change for the return type
),
)
])
)));
}
List _listings = new List();
List<Widget> _getListings() { // <<<<< Note this change for the return type
List listings = List<Widget>();
int i = 0;
for (i = 0; i < 5; i++) {
listings.add(
RadioListTile<String>(
title: const Text('Lafayette'),
value: "c",
groupValue: "x",
onChanged: (_) {
},
),
);
}
return listings;
}
Some things to consider above:
I've made changes to make the code in order to compile and be used for this answer.
added comments for notable changes
List _listings is unused
you can also drop the new keyword when creating new objects (the new version of dart is able to handle this)
Result:
Some comments on the previous answer;
Please do not use unnecessary Containers, if a Container only has a child and nothing else, remove it.
The new keyword does not have to be used, Dart linters even tell not to use it. Like here..
Also if your list does not change you could use a List.unmodifiable like in the example below.
final List<Widget> widgets = List.unmodifiable(() sync* {
for (int i = 0; i < 5; i++) {
yield RadioListTile<String>(
title: const Text('Lafayette'),
value: "c",
groupValue: "x",
onChanged: (_) {
},
);
}
}());
This can be used to avoid unnecessary for loop. Doing the same thing in 2 lines
int numberOfWidgets = 5;
List<Widget> listings = List<Widget>.filled(numberOfWidgets, buildWidget());
This will make a list with exact number of widgets.
Also, this is only helpful if you want similar type of widget in a list