I want to make TextFormField enabled by press a button which is not in the same level,how to pass the value from the button?
return Scaffold(
child:Column(
children:[
Container(
child: Row(
children[
Textbutton(),
.......//otherButtons
]
)
),
DefaultTabController(
child: Column(
children[
TabBar(),
TabBarView(
child:Column(
children[
TextFormField(),
.......//otherTextFormFields
]
)
)
]
)
)
]
)
);
You can set text through TextEditingController. and to disable user input, use readOnly:true. You can test the widget and get the concept how it works.
class ColorTest extends StatefulWidget {
ColorTest({Key? key}) : super(key: key);
#override
State<ColorTest> createState() => _ColorTestState();
}
class _ColorTestState extends State<ColorTest> {
TextEditingController controller = TextEditingController();
bool readOnly = true;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
TextButton(
onPressed: () {
controller.text = "Got the text from button click";
setState(() {});
},
child: Text("set text"),
),
TextFormField(
controller: controller,
readOnly: readOnly,
),
TextButton(
onPressed: () {
readOnly = !readOnly;
setState(() {});
},
child: Text("ReadOnlyMode: $readOnly"),
),
],
)
// materialButto(),
),
);
}
}
Related
i have the following simple full code
import 'dart:developer';
import 'package:flutter/material.dart';
class Test extends StatefulWidget {
const Test({Key? key}) : super(key: key);
#override
State<Test> createState() => _TestState();
}
class _TestState extends State<Test> {
List myListWidget = [];
late bool isColorWhie = false;
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: (){
setState(() {
myListWidget.add(
Container(
width: 50,
height: 50,
color: isColorWhie?Colors.white:Colors.red,
)
);
});
},
child: Scaffold(
backgroundColor: Colors.green,
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
...myListWidget,
TextButton(
onPressed: (){
setState(() {
isColorWhie = !isColorWhie; // here never update
log('done');
});
},
child: const Text('tab to Change color',style: TextStyle(color: Colors.white),)
)
],
),
),
),
);
}
}
i tap on any point on screen to add Container into myListWidget thn call setState(() {}); to update ui.
everything fine now but when i change the isColorWhie to true it should change the color to white but it never update !
i am totally confused why it does not update ? And how could i handle with this ?
For base color change, I am using a separate button, also switching the list value.
One thing variable does update the UI, you need to handle state inside the item(state-management property) or reinitialize the variable to get update state.
class Test extends StatefulWidget {
const Test({Key? key}) : super(key: key);
#override
State<Test> createState() => _TestState();
}
class _TestState extends State<Test> {
List<bool> myListWidgetState = [];
bool isColorWhie = false;
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(
() {
myListWidgetState.add(isColorWhie);
},
);
},
child: Scaffold(
backgroundColor: Colors.green,
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
...myListWidgetState.map(
(e) {
return Container(
width: 50,
height: 50,
color: e ? Colors.white : Colors.red,
);
},
),
TextButton(
onPressed: () {
myListWidgetState = myListWidgetState.map((e) => !e).toList();
setState(() {});
print(isColorWhie);
},
child: const Text(
'tab to Change color',
style: TextStyle(color: Colors.white),
),
),
TextButton(
onPressed: () {
setState(() {
isColorWhie = !isColorWhie;
});
print(isColorWhie);
},
child: const Text(
'tab to Change base color',
style: TextStyle(color: Colors.white),
),
),
],
),
),
),
);
}
}
Since you create a container as an object in GestureDetector and save it to your list, it will not change. It is now permanently saved (of course as long as you do not delete the element) as an entry in your list.
Your logic works exactly as you programmed it. For example, if you were to recompile the app and press the TextButton and then anywhere on your screen, a white container would also appear.
If you want to dynamically change the color of all containers at once, then you can do the following:
class _TestState extends State<Test> {
int containerCounter = 0;
late bool isColorWhie = false;
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
containerCounter++;
});
},
child: Scaffold(
backgroundColor: Colors.green,
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 50,
child: ListView.builder(
shrinkWrap: true,
itemCount: containerCounter,
itemBuilder: ((context, index) {
return Container(
height: 50,
color: isColorWhie ? Colors.white : Colors.red,
);
}),
),
),
TextButton(
onPressed: () {
setState(() {
isColorWhie = !isColorWhie; // here never update
});
},
child: const Text(
'tab to Change color',
style: TextStyle(color: Colors.white),
))
],
),
),
),
);
}
}
I have got a DefaultTabController with a couple of tabs that the user can swipe or press an ElevatedButton to proceed to the next slide, my issue is that I don't know how to change the button's label when the user reaches the last tab using swipes.
Using a stateful widget I managed to change the label when the user presses the button but it doesn't work if the user swipes. Is it possible to change the button when the user reaches the last tab?
class SlidesWidget extends StatelessWidget {
static List<Slide> slides = [
const Slide(
text: 'Welcome to ..'),
const Slide(
text: 'Ready to discover your city?')
];
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: slides.length,
child: Builder( // Builder here, otherwise `DefaultTabController.of(context)` returns null.
builder: (BuildContext context) => Padding(
padding: const EdgeInsets.all(8.0),
child: SafeArea(
child: Column(
children: [
const TabPageSelector(
selectedColor: Colors.white,
),
Expanded(
flex: 100,
child: TabBarView(
children: slides,
),
),
Padding(
padding: const EdgeInsets.all(18.0),
child: ElevatedButton(
onPressed: () {
final TabController controller =
DefaultTabController.of(context)!;
if (!controller.indexIsChanging &&
controller.index < slides.length - 1) {
// Go to next slide if exists
controller.index++;
}
},
child: Text('Next'), // <== on last slide should change label and do other things
),
)
],
),
),
),
),
);
}
}
I will recommend using StatefulWidget, also you can use inline StatefulBuilder to update the UI. And using TabController is handy instead of calling it multiple times, and there is risk of getting null for DefaultTabController.
class SlidesWidget extends StatefulWidget {
SlidesWidget({Key? key}) : super(key: key);
#override
State<SlidesWidget> createState() => _SlidesWidgetState();
}
class _SlidesWidgetState extends State<SlidesWidget>
with SingleTickerProviderStateMixin {
late TabController controller;
List<Slide> slides = [
const Slide(text: 'Welcome to ..'),
const Slide(text: 'Ready to discover your city?')
];
#override
void initState() {
super.initState();
controller = TabController(length: slides.length, vsync: this)
..addListener(() {
setState(() {});
});
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(8.0),
child: SafeArea(
child: Column(
children: [
TabPageSelector(
controller: controller,
selectedColor: Colors.white,
),
Expanded(
flex: 100,
child: TabBarView(
controller: controller,
children: slides,
),
),
Padding(
padding: const EdgeInsets.all(18.0),
child: ElevatedButton(
onPressed: () {
if (!controller.indexIsChanging &&
controller.index < slides.length - 1) {
// Go to next slide if exists
controller.index++;
}
},
child: Text(controller.index == 1 ? 'start' : "Next"), //
),
)
],
),
),
));
}
}
More about TabController and I think you will also like IndexedStack for this case.
I have an app that shows its content in a SingleChildScrollView. There is Container with a transparent color that I'd like to change the color of to red when the SingleChildScrollView is scrolled to any other position than the start position and then change the color back to transparent when the SingleChildScrollView is scrolled back to its starting position. Code:
class App extends StatefulWidget {
const App({Key? key}) : super(key: key);
#override
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Column(
children: [
Flexible(
child: ScrollConfiguration(
behavior: RemoveScrollGlow(),
child: SingleChildScrollView(
child: Column(
children: [
Stack(...) //This is the top section of the page
],
),
),
),
),
],
),
Container(
color: Colors.transparent, //This is the Color I want to change based on the position of the SingleChildScrollView
height: 120,
child: Column(...)
),
],
),
backgroundColor: Colors.white,
);
}
}
EDIT: I managed to make it work by wrapping the SingleChildScrollView in a NotificationListener and updating the color based on the notification like this:
class _AppState extends State<App> {
Color bannercolor = Colors.transparent;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Column(
children: [
Flexible(
child: ScrollConfiguration(
behavior: RemoveScrollGlow(),
child: NotificationListener<ScrollUpdateNotification>(
onNotification: (scrollEnd) {
final metrics = scrollEnd.metrics;
if (metrics.pixels != 0) {
setState(() {
bannercolor = Colors.white;
});
} else {
setState(() {
bannercolor = Colors.transparent;
});
}
return true;
},
child: SingleChildScrollView(
child: Column(
children: [
Column(...),
],
),
),
),
),
),
],
),
Container(
color: bannercolor,
height: 120,
child: Column(...),
),
],
),
backgroundColor: Colors.white,
);
}
}
You can try listening to the scroll controller offset like this
class App extends StatefulWidget {
const App({Key? key}) : super(key: key);
#override
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
final ScrollController _scrollController = ScrollController ();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Column(
children: [
Flexible(
child: ScrollConfiguration(
behavior: RemoveScrollGlow(),
child: SingleChildScrollView(
controller: _scrollController, //add controller here
child: Column(
children: [
Stack(...) //This is the top section of the page
],
),
),
),
),
],
),
AnimatedBuilder(
animation: _scrollController,
builder: (context, _content) {
return Container (
(_scrollController.offset>20)? Colors.blue: Colors.transparent,
height: 120,
child: Column(...)
);
}
),
],
),
backgroundColor: Colors.white,
);
}
}
I have two screens. In screen1 I have an appbar and a card in the body. IF I click the card it should take me to new screen 'screen2'. I am getting the screen2 but I am also getting the appbar from screen1 as well.. I am doing a push from screen1 to screen2.. May I know how to avoid the appbar from the screen1 in screen2? I have onTap method in VideoCard widget which pushes to screen2... it looks like body of the screen1 is being replaced by screen2.. instead i need to push to screen2 from screen1...
enter image description here
class _AllVideosPageTabletState extends State<AllVideosPageTablet> {
bool searchFlag = false;
String searchText = '';
#override
void initState() {
searchFlag = false;
searchText = '';
super.initState();
}
#override
Widget build(BuildContext context) {
final isLandscape = MediaQuery.of(context).orientation ==
Orientation.landscape;
return Scaffold(
body: Container(
padding: EdgeInsets.all(10.0),
child: Column(
children: [
CupertinoSearchTextField(
onChanged: (value) {
if (value != '') {
setState(
() {
searchFlag = true;
searchText = value;
},
);
}
},
onSubmitted: (value) {
if (value != '') {
setState(
() {
searchFlag = true;
searchText = value;
},
);
}
},
),
Divider(),
Row(
children: [
IconButton(
icon: Image.asset('lib/assets/icons/back.png'),
onPressed: () {
Navigator.pop(context);
},
),
Expanded(
child: Container(
child: Center(
child: Text(
'All Videos',
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
color: '#D86300'.toColor(),
),
),
),
),
),
],
),
searchFlag
? SearchResultsListView(searchText)
: Expanded(
child: FutureBuilder(
future: VideoXML().getDataFromXML(context),
builder: (context, data) {
if (data.hasData) {
List<Video> videoList = data.data;
return GridView.count(
scrollDirection: Axis.vertical,
crossAxisCount: isLandscape ? 4 : 2,
crossAxisSpacing: 10.0,
mainAxisSpacing: 10.0,
children: videoList.map(
(video) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: VideoCard(
video.title,
video.name,
video.image,
),
);
},
).toList(),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
),
],
),
),
);
}
Flutter works in a way that every new screen is based on a new Scaffold widget. This way you can customize, whether to show the appBar at all or should be display different appBar on different screens.
For example:
Screen 1 should be a scaffold of its own:
class FirstScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title:'Screen 1'),
body: InkWell(onTap: () => Navigator.of(context).pushedName(routeToSecondScreen),
child: Text('Navigate to secondScreen'),
);
);
}
}
Screen 2 should be a scaffold of its own:
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title:'Screen 2'),
body: Text('Screen 2'),
);
}
}
This way you can have different appBars for each screen. If you do not wish to display appBar on the second screen at all, simply do not specify appBar on the second screen as follows.
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Text('Screen 2'),
);
}
}
I just can't figure out what is the problem with this set state method in flutter. Everything seems okay. But the text is not updating on onPressed.
class NetBalanceWidget extends StatefulWidget {
#override
_NetBalanceWidgetState createState() => _NetBalanceWidgetState();
}
class _NetBalanceWidgetState extends State<NetBalanceWidget> {
#override
Widget build(BuildContext context) {
String text = 'NetBalance-Amount';
return RawMaterialButton(
onPressed: () {
setState(() {
text = 'It works';
});
},
child: Container(
height: 80.0,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(text),
Text('0.00'),
],
),
),
),
);
}
}
You have text as a local variable in the build method. setState is essentially just calling build again, and is resetting the value of text back to its default of 'NetBalance-Amount'.
Move its declaration outside of build:
class _NetBalanceWidgetState extends State<NetBalanceWidget> {
String text = 'NetBalance-Amount';
#override
Widget build(BuildContext context) {
return RawMaterialButton(
onPressed: () {
setState(() {
text = 'It works';
});
},
child: Container(
height: 80.0,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(text),
Text('0.00'),
],
),
),
),
);
}
}