I have 2 Pieces of text, which come from an API call, one in German, one translated in English, they are fed as parameters into a Basic Template, which holds custom styling of the Text. The second Text, the translation is the same colour as the background and when the Reveal button is pressed, I would like the colour to become white (visible). An image for clarity:
How would I change the colour of the second text, I am unsure due to feeding the data through the custom made templates (See code below). Any point in the right direction appreciated.
Main Class
class MistakesScreen extends StatefulWidget {
String username;
MistakesScreen({this.username});
#override
_MistakesScreenState createState() => _MistakesScreenState();
}
class _MistakesScreenState extends State<MistakesScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Hello ${widget.username}"),
),
body: Container(
child: FutureBuilder(
future: fetchGermanPhrases(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Connection State None');
case ConnectionState.active:
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.done:
var gname = snapshot.data['gname'].toString();
var ename = snapshot.data['ename'].toString();
return BasicExerciseTemplate(textOne: gname, textTwo: ename);
default:
return Center(child: CircularProgressIndicator());
}
},
),
),
);
}
}
Templates and Button Code:
class BasicTextTemplate extends StatelessWidget {
final String text;
final Color colour;
BasicTextTemplate({this.text, this.colour});
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(
text,
style: TextStyle(
color: colour,
fontSize: 30,
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic,
),
),
),
width: 350,
decoration: BoxDecoration(
color: Colors.green.shade800,
borderRadius: BorderRadius.circular(8),
),
),
);
}
}
class BasicExerciseTemplate extends StatelessWidget {
final String textOne;
final String textTwo;
BasicExerciseTemplate({this.textOne, this.textTwo});
#override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: BasicTextTemplate(text: textOne, colour: Colors.white),
),
Padding(
padding: const EdgeInsets.all(8.0),
child:
BasicTextTemplate(text: textTwo, colour: Colors.green.shade800),
),
RevealButton()
],
),
);
}
}
class RevealButton extends StatelessWidget {
final String textOne;
final String textTwo;
RevealButton({this.textOne, this.textTwo});
#override
Widget build(BuildContext context) {
return Center(
child: FlatButton(
child: Text('Reveal'),
color: Colors.blueAccent,
textColor: Colors.white,
onPressed: () {
print('Reveal Pressed');
},
),
);
}
}
Try this
isReveal = false;
onPressed: () {
setState(() {
IsReveal = true
});
},
BasicTextTemplate(text: textTwo, colour:!isReveal ? Colors.green.shade800:Colors.white);
To achieve what you are trying, I recommend you to use the Visibility widget. It controls whether the given child is visible or not. I think it'll be much easier.
For instance:
Visibility(
visible: myBoolean,
child: Text(
"Die Deutschen sind sehr nett",
style: TextStyle(fontSize: 20),
),
),
Simply use a function where you change the value of your boolean and trigger the setState method. That way you'll be able to hide or show your texts depending on a condition. For more info, see:
https://api.flutter.dev/flutter/widgets/Visibility-class.html
Related
I need this screen to receive the name (winner X) of the winner that is sent from another screen how can I do this?
on the previous screen I ask the user to type the name and I made an if, if he wins his name appears, I tried with the push navgator and I couldn't
import 'package:flutter/material.dart';
class ganhadorScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Flutter Card Example')),
backgroundColor: Colors.yellow,
body: MyCardWidget(),
),
);
}
}
class MyCardWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 300,
height: 500,
padding: const EdgeInsets.all(10.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
color: Colors.red,
elevation: 10,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
title: Text('PARABENS', style: TextStyle(fontSize: 30.0)),
),
Image.asset("assets/images/trofeu.png"),
Text('winner X',
style: TextStyle(fontSize: 18.0))
],
),
),
),
);
}
}
You can pass data as arguments to another class like this:
class MyClass extends StatelessWidget {
final String data;
final int number;
const MyClass({required this.data, required this.number});
// or like this if you don't like named arguments:
const MyClass(this.data, this.number);
#override
Widget build(BuildContext context) {
return Container();
}
}
So if the navigator pushes MyClass widget to the screen, you will have to write it like this:
MyClass(data: "some data", number: -1);
// or without named parameters, then like this:
MyClass("some data", -1); //note that without named parameters, the order of the parameters is important!
You can pass the values through the arguments property of navigation. In the navigation action of the previous page, however you do it,
Navigator.of(context).pushNamed('MyCardWidget', arguments: variable-that-holds-users-name);
In this screen you recieve the text
final name = ModalRoute.of(context)!.settings.arguments.toString();
Now you can use it
Text(name, style: TextStyle(fontSize 17)),
When I press my card, I want to set my card data into the text field.
my Card Widget and it's another stateful widget. And I need to set this card data into my text field
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
shrinkWrap: true,
children: <Widget>[
Form(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: TextFormField(
decoration: InputDecoration(
labelText: "CupCake Name",
controller: cupCake,
onChanged: (cupcake) {
cupcakeName = cupcake;
},
),
),
),
]
LoadData(),
),
);
}
This is my Card Widget and it's another stateful widget. And I need to set this card data into my text field
class LoadData extends StatefulWidget {
const LoadData({Key? key}) : super(key: key);
#override
_LoadDataState createState() => _LoadDataState();
}
class _LoadDataState extends State<LoadData> {
#override
Widget build(BuildContext context) {
return Container(
child: ListView(
return GestureDetector(
onTap: () => {print("Test")},
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
child: Container(
padding: EdgeInsets.all(16),
child: Column(
children: <Widget>[
Text(
data['cupcake_name'],
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
data['description'],
style: TextStyle(
fontSize: 20,
),
),
],
);
}).toList(),
),
}
}
Take a look at flutter simple state management. Basically as you have state that is shared and pertinent to a sub-tree of your widget tree you should encapsulate and lift that state as high as the first widget that needs to interact with it.
1 create the model which will handle the needed state
class CupCakeState extends ChangeNotifier {
CupCake? cupcake;
void selectCupcake(CupCake newCake){
this.cupCake=newCupcake;
notifyListeners();
}
2 Provide the model in the widget tree
ChangeNotifierProvider(
create: (context) => CupCakeState(),
child: const MyApp(),
),
3 Now you can use it either reading the context.
In your card:
class _LoadDataState extends State<LoadData> {
#override
Widget build(BuildContext context) {
return Container(
child: ListView(
return GestureDetector(
onTap: () => {context.read<CupCakeState>().selectCupcake(CupCake(name:data['cupcake_name'],price:data['cupcake_price']))},
...
Or creating a widget that rebuilds everytime you call notifylisteners()
Consumer<CupCakeState>(
builder: (context, cupcpakeState, child) {
cupcake.value=cupcpakeState.price;//assign the price to the controller
return TextFormField(
decoration: InputDecoration(
labelText: "CupCake Name",
controller: cupCake,
onChanged: (cupcake) {
cupcakeName = cupcake;
},
),
},
)
I am pretty new in flutter. I don't know what happening in background because after hot reload its work fine. On another dart files that happens, firebase dont provide me data on initialization just after hot reload.
class CityServices {
getCites() {
return Firestore.instance.collection('cities').getDocuments();
}
}
class _HomeScreenState extends State<HomeScreen> {
bool citiesFlag = false;
var cities;
int citiesCount;
String actualCity;
Maybe mistake is here.
#override
void initState() {
super.initState();
CityServices().getCites().then((QuerySnapshot) {
if (QuerySnapshot.documents.isNotEmpty) {
citiesFlag = true;
cities = QuerySnapshot.documents;
citiesCount = QuerySnapshot.documents.length;
}
});
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: MyColors.vintageGreen,
appBar: AppBar(
backgroundColor: MyColors.background,
title: Center(
child: Text(
'Válasszon települést...',
style: GoogleFonts.barlowCondensed(
color: MyColors.appbarText,
fontSize: 26.0,
fontWeight: FontWeight.w500),
),
),
),
body: Center(
child: Container(
child: GridView.count(
crossAxisCount: 2,
children: List.generate(citiesCount, (index) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
),
child: InkWell(
onTap: () {
actualCity = cities[index]['city_name'];
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
CityView(cityName: actualCity)),
);
},
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
title: Center(
child: Text(
cities[index]['city_name'],
style: TextStyle(
fontWeight: FontWeight.w500, fontSize: 18.0),
)),
subtitle: Center(child: Text('22 bejegyzés')),
),
Flexible(
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(5)),
child: Padding(
padding: EdgeInsets.only(bottom: 15.0),
child: Image(
image: AssetImage(
cities[index]['img_path'],
),
),
),
),
)
],
),
),
color: MyColors.background,
);
}),
),
),
),
),
);
}
}
Maybe here is the mistake? Should it be on top of dart file?
class HomeScreen extends StatefulWidget {
static const String id = 'home';
#override
_HomeScreenState createState() => new _HomeScreenState();
}
Let me explain the issue and why it is happening, then propose few solutions.
inside initState you are calling CityServices().getCites().then... which is an async method.
However, when your widget is built for the first time, the data you expect from Firestore is not ready yet, thus you get null for both cities and citiesCount.
Short term solution:
make sure there is null check, display indicator while waiting for the data.
body: Center(
child: (cities == null) ?
CircularProgressIndicator()
: Container(...
Additionally, you can also refactor your initState to something like this
void getCities() async {
var snapshot CityServices().getCites();
setState(() {
citiesFlag = true;
cities = snapshot.documents;
citiesCount = snapshot.documents.length;
});
}
#override
void initState() {
getCities();
super.initState();
}
Long term solution:
use BLoC pattern and make data loading decoupled from UI.
see flutter_bloc for how to implement it.
I've created a button that allows the user to add a credit card, the cards are being added to a Listview.builder.
The problem is that when I have multiple cards and I select one, it selects all of them, it's probably a state problems but I didn't find (yet) how to fix it, here is the dartpad : [dartpad][1] of my code if you can check it and maybe show me what I'm doing wrong, it's probably failing inside the buildBody but I'm not really sure and I have not successfully found a solution yet.
You simply have to tap two times on 'Add a card' and you will see when checking one of them, both will get selected.
For some reason the link isnt working through the shortcut so here it is https://dartpad.dev/b0aaaa2901aa3ac67426d9bdd885abb1:
I modified your dartpad code to get the behaviour you are trying to achive:
The code is provided below:
The issue was that you are using the same bool value _isSelected for the two Checkboxes.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: InformationsBancairesPage(),
),
),
);
}
}
class InformationsBancairesPage extends StatefulWidget {
#override
_InformationsBancairesPageState createState() =>
_InformationsBancairesPageState();
}
class _InformationsBancairesPageState extends State<InformationsBancairesPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text(
'Payer ou recevoir un paiement'.toUpperCase(),
style: TextStyle(fontSize: 19, color: Colors.black),
),
centerTitle: true,
backgroundColor: Colors.white,
iconTheme: IconThemeData(color: Colors.black),
),
body: Padding(
padding: const EdgeInsets.all(15.0),
child: ListView(
children: <Widget>[
InputAddCarte(),
],
),
),
);
}
}
class InputAddCarte extends StatefulWidget {
#override
_InputAddCarteState createState() => _InputAddCarteState();
}
class _InputAddCarteState extends State<InputAddCarte> {
// create a list of bool values for your checkboxes
List<bool> _selectedList = [false, false];
int value = 0;
void initState() {
super.initState();
}
_addCard() {
setState(() {
value = value + 1;
print(value);
});
}
Widget buildBody(BuildContext context, int indexClicked) {
return LabeledCheckbox(
label: 'Card credit',
padding: const EdgeInsets.symmetric(horizontal: 20.0),
// pass the value of the checkbox at the selected index
value: _selectedList[indexClicked],
onChanged: (bool newValue) {
setState(() {
// pass the value of the checkbox at the selected index
_selectedList[indexClicked] = newValue;
});
},
);
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
ButtonTheme(
minWidth: 250,
child: RaisedButton(
color: Color(0xff00cc99),
child: Text(
'ADD A CARD'.toUpperCase(),
style: TextStyle(color: Colors.white, fontSize: 18),
),
onPressed: _addCard,
),
),
// Show the cards when you press 'Ajouter une carte'
ListView.builder(
shrinkWrap: true,
itemCount: this.value,
itemBuilder: (BuildContext context, int value) {
// display two cards maximum
if (value < 2) {
// pass the index of the selected checkbox
return buildBody(context, value);
}
return Container();
},
),
ButtonTheme(
minWidth: 250,
child: RaisedButton(
color: Colors.orange,
child: Text(
'Delete a card'.toUpperCase(),
style: TextStyle(color: Colors.white, fontSize: 18),
),
onPressed: () {},
),
)
],
);
}
}
// Create custom checkbox for the list of cards
class LabeledCheckbox extends StatelessWidget {
const LabeledCheckbox({
this.label,
this.padding,
this.value,
this.onChanged,
});
final String label;
final EdgeInsets padding;
final bool value;
final Function onChanged;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
onChanged(!value);
},
child: Padding(
padding: padding,
child: Row(
children: <Widget>[
Expanded(child: Text(label)),
Checkbox(
value: value,
onChanged: (bool newValue) {
onChanged(newValue);
},
),
],
),
),
);
}
}
I was trying to get the list page refreshed if a method was run on another page. I do pass the context using the push navigation.
I tried to follow these 3 answers Answer 1 Answer 2 and Answer 3 and I am not able to manage the states here.
This is the first list page which needs to be refreshed. It calls a class
class _PageLocalState extends State<PageLocal> {
#override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: SafeArea(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: widget.allLocal.length,
//padding: const EdgeInsets.only(top: 10.0),
itemBuilder: (context, index) {
return LocalCard(widget.allLocal[index]);
},
)),
)
],
),
);
}
}
The next class:
class LocalCardState extends State<LocalCard> {
FavData localdet;
LocalCardState(this.localdet);
ListTile makeListTile() => ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
title: Text(
localdet.name,
style: TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(localdet.loc),
trailing: Icon(Icons.keyboard_arrow_right, size: 30.0),
onTap: () => navigateToDetail(localdet),
);
Widget get localCard {
return new Card(
elevation: 4.0,
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Container(
child: makeListTile(),
));
}
#override
Widget build(BuildContext context) {
return new Container(
child: localCard,
);
}
navigateToDetail(FavData localdet) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FavouriteDetailPage(
mndet: localdet,
)));
setState(() {});
}
}
Now this is routing to the final detail page:
class _FavouriteDetailPageState extends State<FavouriteDetailPage> {
bool isFav = false;
FavData mndet;
_FavouriteDetailPageState(this.mndet);
// reference to our single class that manages the database
final dbHelper = DatabaseHelper.instance;
#override
Widget build(BuildContext context) {
Widget heading = new Container(...);
Widget middleSection = new Expanded(...);
Widget bottomBanner = new Container(...);
Widget body = new Column(...);
final makeBottom = Container(
height: 55.0,
child: BottomAppBar(
color: Color.fromRGBO(36, 36, 36, 1.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new FavIconWidget(mndet),
],
),
),
);
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('The Details'),
backgroundColor: Color.fromRGBO(36, 36, 36, 1.0),
),
body: Container(
child: Card(
elevation: 5.0,
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.white70, width: 1),
borderRadius: BorderRadius.circular(10),
),
margin: EdgeInsets.all(20.0),
child: Padding(
padding: new EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
child: body,
),
),
),
bottomNavigationBar: makeBottom,
);
}
void share(BuildContext context, FavData mndet) {
final RenderBox box = context.findRenderObject();
final String shareText = "${mndet.name} - ${mndet.desc}";
Share.share(shareText,
subject: mndet.loc,
sharePositionOrigin: box.localToGlobal(Offset.zero) & box.size);
}
}
class FavIconWidget extends StatefulWidget {
final FavData mnforIcon;
FavIconWidget(this.mnforIcon);
#override
_FavIconWidgetState createState() => _FavIconWidgetState();
}
class _FavIconWidgetState extends State<FavIconWidget> {
final dbHelper = DatabaseHelper.instance;
Future<bool> get isFav async {
final rowsPresent = await dbHelper.queryForFav(widget.mnforIcon.id);
if (rowsPresent > 0) {
print('Card Loaded - Its Favourite already');
return false;
} else {
print('Card Loaded - It is not favourite yet');
return true;
}
}
void _insert() async {...}
void _delete() async {...}
#override
Widget build(BuildContext context) {
return FutureBuilder<bool>(
future: isFav,
initialData:
false, // you can define an initial value while the db returns the real value
builder: (context, snapshot) {
if (snapshot.hasError)
return const Icon(Icons.error,
color: Colors.red); //just in case the db return an error
if (snapshot.hasData)
return IconButton(
icon: snapshot.data
? const Icon(Icons.favorite_border, color: Colors.white)
: Icon(Icons.favorite, color: Colors.red),
onPressed: () => setState(() {
if (!snapshot.data) {
print('Its favourite so deleting it.');
_delete();
} else {
print('Wasnt fav in the first place so inserting.');
_insert();
}
}));
return CircularProgressIndicator(); //if there is no initial value and the future is not yet complete
});
}
}
I am sure this is just some silly coding I have done but just not able to find out. Where.
I tried adding Navigator.pop(context); in different sections of the detail page and it fails.
Currently, I have to navigate back to the Favourites list page and then HomePage and then back to Favourites ListPage to refresh the list.
try this.. Anywhere you are using Navigator.pop or Navigator.push .. Instead of this use this:
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (BuildContext context) => Password())
);
//instead of Password use the name of the page(the second page you want to go to)