I was told that 25th line of code contains an issue where setState() with random color being used. Help identify an issue - flutter

Original task sounded like:
The application should: display the text "Hey there" in the middle of
the screen and after tapping anywhere on the screen a background color
should be changed to a random color. You can also add any other
feature to the app - that adds bonus points Please do not use any
external libraries for color generation
My solution (GitHub):
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: RandomBackgroundColorWidget(),
);
}
}
class RandomBackgroundColorWidget extends StatefulWidget {
#override
_RandomBackgroundColorWidget createState() => _RandomBackgroundColorWidget();
}
class _RandomBackgroundColorWidget extends State<RandomBackgroundColorWidget> {
int _colorIndex = 0xFF42A5F5;
void _randomColorIndexGenerator() {
final _rng = new Random();
setState(() => {_colorIndex = (_rng.nextInt(0xFFFFFF) + 0xFF000000)});
}
#override
Widget build(BuildContext context) {
return Stack(children: [
Material(
color: Color(_colorIndex),
child: Center(
child: Text("Hey there"),
),
),
GestureDetector(
onTap: () => _randomColorIndexGenerator(),
),
]);
}
}
While reviewing my test task interviewer said that 25th line of code contains an issue.
setState(() => {_colorIndex = (_rng.nextInt(0xFFFFFF) + 0xFF000000)});
And he commented:
"It is working in a way that is not intended by you."
Help to identify an issue in 25th line of code.

You are accidentally combining the two ways to declare a function in Dart: the arrow operator => and curly braces {}.
Line 25 should be:
setState(() => _colorIndex = _rng.nextInt(0xFFFFFF) + 0xFF000000);
with no extra curly braces.

The issue is a syntax error. When using setState() =>, you dont need the {}
setState(() {_colorIndex = (_rng.nextInt(0xFFFFFF) + 0xFF000000)});
or
setState(() => _colorIndex = (_rng.nextInt(0xFFFFFF) + 0xFF000000));

I couldn't find the error you mention, however I recommend that you always use init state when assigning default values.
Here other way
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: RandomBackgroundColorWidget(),
);
}
}
class RandomBackgroundColorWidget extends StatefulWidget {
#override
_RandomBackgroundColorWidget createState() => _RandomBackgroundColorWidget();
}
class _RandomBackgroundColorWidget extends State<RandomBackgroundColorWidget> {
Color _color;
#override
void initState() {
_color = Colors.white;
super.initState();
}
void getrandomColor() {
setState(() {
_color = Colors.primaries[Random().nextInt(Colors.primaries.length)];
});
}
#override
Widget build(BuildContext context) {
return Stack(children: [
Material(
color: _color,
child: Center(
child: Text("Hey there"),
),
),
GestureDetector(
onTap: getrandomColor,
),
]);
}
}

Issue is in nextInt function.
Flutter accepts color codes for generation up to 0xFFFFFFFF, where first pair of numbers is needed for opacity level and other pairs needed for RGB levels
nextInt function generates random number in range up to, but not including, passed number. For example, nextInt(3) will generate randomly 0,1 or 2, but not 3.
So original app was generating all random colors (from 0x0 to 0xFFFFFE) except last one - 0xFFFFFF
Therefore, 25th line should look like this in order to generate every possible color.
setState(() => _colorIndex = (_rng.nextInt(0x1000000) + 0xFF000000));

Related

how to draw a line with two point in it in flutter

I have two dropdown button that represt time (0-23) and the user can choose a time range for the light to be on. I want to represent the choosen times by a line like below where the the red circles move when they choose different time. it does not need to be circle, anything just to represent the range of time.
anyone have an idea or comments on that?
Use RangeSlider
One option is to use the RangeSlider in Flutter, to select an range of two values.
One caveat of the solution is that the range values are only visible while moving the thumb-selectors. Albeit you could use the callback function in the example to show the selected range elsewhere.
Example:
import 'package:flutter/material.dart';
// This StatefulWidget uses the Material RangeSlider and provides a
// callback for the updated range.
class SliderExample extends StatefulWidget {
const SliderExample({
Key? key,
required this.onRangeSelected,
required this.values
}) : super(key: key);
final RangeValues values;
final Function(RangeValues) onRangeSelected;
#override
State<SliderExample> createState() => _SliderExampleState();
}
class _SliderExampleState extends State<SliderExample> {
late RangeValues selectedRange;
#override
void initState() {
super.initState();
selectedRange = widget.values;
}
#override
Widget build(BuildContext context) {
return RangeSlider(
values: selectedRange,
onChanged: (range) {
setState(() {
selectedRange = range;
});
widget.onRangeSelected(range);
},
min: widget.values.start,
max: widget.values.end,
divisions: 24,
labels: RangeLabels("${selectedRange.start.round()}", "${selectedRange.end.round()}"),
);
}
}
// Using the SliderExample widget we just defined:
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SliderExample(
values: const RangeValues(0, 23),
onRangeSelected: (range) => print("${range.start} - ${range.end}"),
),
],
),
),
);
}
}
main() => runApp(const App());
Other approaches
Time Picker
However, you might want to consider using a time-picker instead if you want more fine-tuned control for the time: https://material.io/components/time-pickers/flutter#using-time-pickers
You can use RangeSlider Widget
https://api.flutter.dev/flutter/material/RangeSlider-class.html

"RangeError (index): Index out of range: no indices are valid: 0" when trying to get a barcode

In Flutter I actually write a smartphone app for my company. There is also a barcode reader to legitimize customers to enter the store.
The problem is that whenever I want to convert a 13 number string into a barcode I get the error shown above.
For first time using the App the customer types his number into a TextField. The number (originally a String) will be stored in a private variable _number (also from type String) for further processing.
Even if I use my function
convert2barcode('1111111111116')
the barcode is shown correctly, but not for
convert2barcode(_number)
I have no idea why. If I write
print(_number)
I get the correct result (e.g. '1111111111116').
Hope you can help. Tell me if you need more code information.
Best regards.
EDIT
I wrote a more simple example to understand what I mean.
I've a TextField where a numeric String will be converted into a barcode at submitting.
import 'package:flutter/material.dart';
import 'barcode.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: GUI(),
);
}
}
class GUI extends StatefulWidget {
GUI({Key? key}) : super(key: key);
#override
State<GUI> createState() => _GUI();
}
class _GUI extends State<GUI> {
bool _isVisible = false;
String numer = "";
void showWidget() {
setState(() {
_isVisible = !_isVisible;
});
}
#override
Widget build(BuildContext contet) {
return Scaffold(
body: Visibility(
child: Row(
children: /*[
Text(_numer.runtimeType.toString())
]*/
Barcode.convert2Barcode(numer),
),
visible: _isVisible,
replacement: Column(
children: [
TextField(
onSubmitted: (value) {
numer = value;
showWidget();
},
),
],
),
),
);
}
}
This is the code which caused the exception:
Barcode.convert2Barcode(numer),
If I run the code I'll get the error shown in the headline.
Only If I write
convert2barcode('1111111111116'),
the barcode will be shown correctly.
I have output the type of the variable with runtimeType for both ('1111111111116' and _numer). It's always the same (String). Even if I make something like this
_numer = '1111111111116';
print(_numer);
I will get the correct result.
I have really no idea what this exception cause.
When TextField's onSubmit is not called, the number is empty. That's the reason you get the error.
Replace
Barcode.convert2Barcode(numer),
with
if (number.isNotEmpty)
Barcode.convert2Barcode(numer),

How to set a variable value in a widget and in another widget?

I'm trying to set a variable value (number, in the code below) that exists in FirstWidget, from SecondWidget. And notify both widgets so the number will be updated in the two widgets.
class FirstWidget extends StatefulWidget {
#override
_FirstWidgetState createState() => _FirstWidgetState();
}
class _FirstWidgetState extends State<FirstWidget> {
int number = 0;
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(
'$number',
),
SecondWidget(),
Text(
'$number',
)
],
);
}
}
class SecondWidget extends StatefulWidget {
#override
_SecondWidgetState createState() => _SecondWidgetState();
}
class _SecondWidgetState extends State<SecondWidget> {
#override
Widget build(BuildContext context) {
return TextButton(
child: Text('The number is $number. Press to increase the number'),
onPressed: () {
setState(() {
number++;
});
},
);
}
}
(I know that that code gives an error, but the main idea was to give you the problem I want to solve).
The output I want it to be shown:
before pressing the button -
0
The number is 0. Press to increase the number
0
after pressing the button -
1
The number is 1. Press to increase the number
1
So I would be happy if you can help solving this.
Thanks.
There are many approaches to get the result you are looking for, this one is using ValueNotifier in order to change the value of number
Here is an example based on your code:
https://dartpad.dev/b6409e10de32b280b8938aa75364fa7b
We can use another State Management like Provider or Cubit, and we will get the same result.
Another way is to pass a function as a param in the second widget and execute that function when button is pressed

List.remove() removes the correct object but the object's state in the list shifts as if last entry is being deleted

So I have been trying to solve this issue for a couple of days now and I seem to have hit a real dead end here.
The issue (which I have simplified in the code below) is that when I try to remove an item from a List<"SomeDataObject">, it does actually remove the correct object from the list. This is evident because the ID that I arbitrarily assigned to the objects does shift just as I would expect it to when I remove something. However, oddly enough, even though the IDs shift in the list and the correct ID is removed, the states of all the widgets seem to act as though the last item is always removed, even when the deleted object is at the middle or even beginning of the list.
An example would be if the list looked as such:
Data(id: 630) // This one starts blue
Data(id: 243) // Let's say the user made this one red
Data(id: 944) // Also blue
And let's say I tried to remove the middle item from this list. Well what happens is that the list will look like this now:
Data(id: 630)
Data(id: 944)
This seems at first to be great because it looks like exactly what I wanted, but for some reason, the colors did not change their order and are still Red first, then Blue. The color state data seems to function independently from the actual objects and I could not find a clear solution.
I have code of an example program to reproduce this problem and I also have some pictures below of the program so that it is clearer what the issue is.
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: HomeScreen(),
);
}
}
// Home screen class to display app
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
List<DataWidget> widgetsList = List<DataWidget>();
// Adds a new data object to the widgets list
void addData() {
setState(() {
widgetsList.add(DataWidget(removeData));
});
}
// Removes a given widget from the widgets list
void removeData(DataWidget toRemove) {
setState(() {
widgetsList = List.from(widgetsList)..remove(toRemove);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
/* I used a SingleChildScrollView instead of ListView because ListView
resets an objects state when it gets out of view so I wrapped the whole
list in a column and then passed that to the SingleChildScrollView to
force it to stay in memory. It is worth noting that this problem still
exists even when using a regular ListView. */
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
// Added a SizedBox just to make column take up whole screen width
children: [...widgetsList, SizedBox(width: double.infinity)],
),
),
// FloatingActionButton just to run addData function for example
floatingActionButton: FloatingActionButton(onPressed: addData),
);
}
}
// Simple class that is a red square that when clicked turns blue when pressed
class DataWidget extends StatefulWidget {
DataWidget(this.onDoubleTap);
final Function(DataWidget) onDoubleTap;
// Just a random number to keep track of which widget this is
final String id = Random.secure().nextInt(1000).toString();
#override
String toStringShort() {
return id;
}
#override
_DataWidgetState createState() => _DataWidgetState();
}
class _DataWidgetState extends State<DataWidget> {
/* Here the example state information is just a color, but in the full version
of this problem this actually has multiple fields */
Color color = Colors.red;
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
// onDoubleTap this passes itself to the delete method of the parent
onDoubleTap: () => widget.onDoubleTap(widget),
// Changes color on tap to whatever color it isn't
onTap: () {
setState(() {
color = color == Colors.red ? Colors.blue : Colors.red;
});
},
child: Container(
child: Center(child: Text(widget.id)),
width: 200,
height: 200,
color: color,
),
),
);
}
}
Before user changes any colors (object ID is displayed as text on container):
User changes middle container to blue by tapping:
User attempts to delete middle (blue) container by double tapping:
As you can see from the image above, the ID was deleted and the ID from below was moved up on the screen, but the color information from the state did not get deleted.
Any ideas for things to try would be greatly appreciated.
You need to make a few changes to your DataWidget class to make it work:
// Simple class that is a red square that when clicked turns blue when pressed
class DataWidget extends StatefulWidget {
Color color = Colors.red; // moved from _DataWidgetState class
DataWidget(this.onDoubleTap);
final Function(DataWidget) onDoubleTap;
// Just a random number to keep track of which widget this is
final String id = Random.secure().nextInt(1000).toString();
#override
String toStringShort() {
return id;
}
#override
_DataWidgetState createState() => _DataWidgetState();
}
class _DataWidgetState extends State<DataWidget> {
/* Here the example state information is just a color, but in the full version
of this problem this actually has multiple fields */
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
// onDoubleTap this passes itself to the delete method of the parent
onDoubleTap: () => widget.onDoubleTap(widget),
// Changes color on tap to whatever color it isn't
onTap: () {
setState(() {
widget.color =
widget.color == Colors.red ? Colors.blue : Colors.red;
});
},
child: Container(
child: Center(child: Text(widget.id)),
width: 200,
height: 200,
color: widget.color,
),
),
);
}
}

Listview not showing when code for state is defined in a seperate file

am new to Flutter development, and this might be a naive question, but i was following the beginner tutorial on flutter dev site, and my listview is not showing. I have changed some class/function names,and put the code in different files, but am not sure what is being wrong here, This is supposed to be a list of infinite list of random words.
Here is the complete code:
//File name: main.dart
import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app_as/StatefulWidgetStates.dart';
class StatefulRandomWordsWidget extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return RandomWordsState();
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
var wordWidget = StatefulRandomWordsWidget();
return MaterialApp(
title: 'Useless Title',
home: Scaffold(
appBar: AppBar(title: Text('Welcome to Flutter')),
body: wordWidget));
}
}
void main() {
runApp(MyApp());
}
and
//File name: StatefulWidgetStates.dart
import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app_as/main.dart';
class RandomWordsState extends State<StatefulRandomWordsWidget> {
final dataList = <WordPair>[];
#override
Widget build(BuildContext context) {
return getListView();
}
Widget getListView() {
ListView listViewWidget = ListView.builder(
itemCount: dataList.length,
padding: EdgeInsets.all(8.0),
itemBuilder: (context, pos) {
if (pos.isOdd) {
return Divider();
}
else {
final index = pos ~/ 2;
if (index >= dataList.length) {
List<WordPair> pairs=generateWordPairs().take(10);
dataList.addAll(pairs);
}
WordPair childData = dataList[index];
return ListTile(title: Text(childData.asCamelCase, style: TextStyle(fontSize: 12.0)));
}
});
return listViewWidget;
}
}
I also don't understand whats this final index = pos ~/ 2; logic about. The official docs say:
The expression i ~/ 2 divides i by 2 and returns an integer result. For example: 1, 2, 3, 4, 5 becomes 0, 1, 1, 2, 2. This calculates the actual number of word pairings in the ListView, minus the divider widgets.
But am guessing am using it wrong.
Remember that when you are working with StateFul Widgets, you need to tell the framework that some state(date) in that widget has changed and it needs to be rebuild. So when you are adding thing to te list, the state changes and it needs to be re-builded. You tell the framework to rebuild by calling the setState((){
}); method.
example:
List<WordPair> pairs = generateWordPairs().take(10);
dataList.addAll(pairs);
setState(() {});