How to update screen when instance of external stateful widget class is updated - flutter

I am displaying the weight of an instance of a person class on my homepage. When I update the weight of this instance through a form in a popup bottom sheet the displayed weight is only changed after a hot reload. How can I trigger a setState in my person class when its instances parameters are changed in homepage?
main.dart
import 'package:flutter/material.dart';
import 'package:metricwidget/screens/homepage.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// Root of application
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const Homepage(),
);
}
}
person.dart
import 'package:flutter/material.dart';
class person extends StatefulWidget {
int? weight;
person({Key? key, this.weight}) : super(key: key);
void updateWeight(newWeight){
weight = newWeight;
}
#override
_personState createState() => _personState();
}
class _personState extends State<person> {
#override
Widget build(BuildContext context) {
return Center(
child: Text(
widget.weight.toString(),
style: const TextStyle(fontSize: 24),
),
);
}
}
homepage.dart
import 'package:mvs/person.dart';
import 'package:flutter/material.dart';
class Homepage extends StatefulWidget {
const Homepage({Key? key}) : super(key: key);
#override
_HomepageState createState() => _HomepageState();
}
class _HomepageState extends State<Homepage> {
var joe = person(weight: 23);
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Material(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
child: joe,
),
OutlinedButton(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (context) {
return Form(
key: _formKey,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(12.0),
child: TextFormField(
onSaved: (String? value) {
if (int.parse(value!) > 0) {
setState(() {
joe.updateWeight(int.parse(value));
});
}
},
keyboardType: TextInputType.number,
maxLength: 3,
initialValue: joe.weight.toString(),
decoration: const InputDecoration(
icon: Icon(Icons.label),
),
validator: (value) {
if (value!.isEmpty) {
return "Please enter value";
}
return null;
},
),
),
OutlinedButton(
onPressed: () {
_formKey.currentState!.save();
Navigator.pop(context);
},
child: const Text("submit"),
)
],
),
);
},
);
},
child: const Text("Update"),
)
],
),
);
}
}

Was able to solve this using provider and changenotifier, same as the format outlined in the docs below
Reference: https://pub.dev/packages/provider

Related

How to continuously get whether the TextField's text is empty in Flutter?

I have a TextField. I want its text not to be empty. (so I want to know if the text is empty)
I have tried using the following code, but it doesn't work:
controller.text.trim().isEmpty()
My code:
TextFormField(
controller: controller,
),
controller.text.trim().isEmpty()
How to continuously get whether the TextField's text is empty in Flutter? I would appreciate any help. Thank you in advance!
full example:
code:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController _controller = TextEditingController();
String _text = '';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter Demo Home Page'),
),
body: Container(
padding: const EdgeInsets.all(16),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_text),
const SizedBox(height: 20),
TextField(
controller: _controller,
onChanged: (value) {
setState(() {
_text = value;
});
},
decoration: const InputDecoration(
hintText: 'Enter text',
),
),
// submit
ElevatedButton(
onPressed: _text.isEmpty
? null
: () {
setState(() {
_text = _controller.text;
});
},
child: const Text('Submit'),
),
],
),
),
),
);
}
}
It can be done without any temporary variable using ValueListenableBuilder
After some research figured out
controller.text by itself is not listenable
TextEditingController extends ValueNotifier<TextEditingValue> i.e you can use ValueListenableBuilder from material package to listen to text changes.
Code:
class _MyWidgetState extends State<MyWidget> {
late TextEditingController textEditingController;
#override
void initState() {
textEditingController = TextEditingController();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
TextField(
controller: textEditingController,
),
ValueListenableBuilder<TextEditingValue>(
valueListenable: textEditingController,
builder: (context, value, child) {
return ElevatedButton(
onPressed: value.text.isNotEmpty ? () {} : null,
child: const Text('I am disabled when text is empty'),
);
},
),
],
),
),
);
}
}
Without text:
With text:
You can add listener to your TextEditingController and call setState to update the UI.
late TextEditingController controller = TextEditingController()..addListener(() {
setState((){}); // to update the ui
});
The place you will use controller.text.trim().isEmpty() will show the updated state.
Example
class Test extends StatefulWidget {
const Test({super.key});
#override
State<Test> createState() => _TestState();
}
class _TestState extends State<Test> {
late TextEditingController controller = TextEditingController()
..addListener(() {
setState(() {}); // to update the ui
});
#override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
controller: controller,
),
ElevatedButton(
onPressed: controller.text.trim().isEmpty ? null : () {},
child: Text("Button"))
],
);
}
}

Flutter toggle button using Getx for state management

I'm making a toggle button to switch between the unit system, I need to do it using Getx for state management.
This code works, but its using setState() instead
This is the (simplified) code:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_toggle_tab/flutter_toggle_tab.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({this.title});
final String? title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _tabTextIndexSelected = 0;
final _listTextTabToggle = ["km / m", "m / ft"];
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: SingleChildScrollView(
child: Column(
children:[
FlutterToggleTab(
selectedIndex: _tabTextIndexSelected,
selectedBackgroundColors: const [
Colors.blue,
Colors.blueAccent
],
selectedTextStyle: const TextStyle(
color: Colors.white),
unSelectedTextStyle: const TextStyle(),
labels: _listTextTabToggle,
selectedLabelIndex: (index) {
setState(() {
_tabTextIndexSelected = index;
});
},
isScroll: false,
),
Text(
"Index selected : $_tabTextIndexSelected",
),
],
),
),
),
);
}
}
Tried to add obs to the variable _tabTextIndexSelected and obx to everything that is supposed to change, but it doesn't work.
Also, I'm using https://pub.dev/packages/flutter_toggle_tab
this is what I tried (two codes are from different files, I like to try first rather than doing it in my project):
RxInt _tabTextIndexSelected = 0.obs;
final _listTextTabToggle = ["km / m", "m / ft"];
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Column(
children: [
Obx(
()=> FlutterToggleTab(
selectedIndex: _tabTextIndexSelected,
selectedBackgroundColors: const [
Colors.blue,
Colors.blueAccent
],
selectedTextStyle: const TextStyle(
color: Colors.white),
unSelectedTextStyle: const TextStyle(),
labels: _listTextTabToggle,
selectedLabelIndex: (index) {
_tabTextIndexSelected = index.obs;
},
isScroll: false,
),
),
Obx(
()=>Text(
"Index selected : $_tabTextIndexSelected",
),
),
The reactive variable and list of tabs string declaration inside the getx controller.
Below is the working snippet to toggle the tabbar.
import 'package:flutter/material.dart';
import 'package:flutter_toggle_tab/flutter_toggle_tab.dart';
import 'package:get/get.dart';
class TestController extends GetxController {
final listTextTabToggle = ["km / m", "m / ft"];
RxInt tabTextIndexSelected = 0.obs;
toggle(int index) => tabTextIndexSelected.value = index;
}
class TestPage extends StatelessWidget {
const TestPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final ctrl = Get.put(TestController());
return SafeArea(
child: Scaffold(
body: Column(children: [
Obx(
() => FlutterToggleTab(
selectedIndex: ctrl.tabTextIndexSelected.value,
selectedBackgroundColors: const [Colors.blue, Colors.blueAccent],
selectedTextStyle: const TextStyle(color: Colors.white),
unSelectedTextStyle: const TextStyle(),
labels: ctrl.listTextTabToggle,
selectedLabelIndex: (index) => ctrl.toggle(index),
isScroll: false,
),
),
Obx(
() => Text(
"Index selected : ${ctrl.tabTextIndexSelected.value}",
),
)
])),
);
}
}
Output:

How to create type of textfield / textbox it shouldn't be a single widget

How to create type of Text field? it shouldn't be a single widget. is there any way to create like this?
this might help.textformfield
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
#override
Widget build(BuildContext context) {
return Material(
child: Center(
child: Shortcuts(
shortcuts: const <ShortcutActivator, Intent>{
// Pressing space in the field will now move to the next field.
SingleActivator(LogicalKeyboardKey.space): NextFocusIntent(),
},
child: FocusTraversalGroup(
child: Form(
autovalidateMode: AutovalidateMode.always,
onChanged: () {
Form.of(primaryFocus!.context!)!.save();
},
child: Wrap(
children: List<Widget>.generate(5, (int index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ConstrainedBox(
constraints: BoxConstraints.tight(const Size(200, 50)),
child: TextFormField(
onSaved: (String? value) {
debugPrint(
'Value for field $index saved as "$value"');
},
),
),
);
}),
),
),
),
),
),
);
}
}

Why is the refreshing pull in the App not working?

I'm building my app with Flutter 2.10.5 and Dart 2.16.2.
When i try to refresh the demo content whith a pull, nothing happens. I have multiple navigation routes for different content. So the demo is a litte bit complex.
The main.dart includes the basic code for the app. I use the NavDrawer Widget to build the different pages. Every route is defined in the navigation.dart file, which reference to the content widgets.
My code so far is:
import 'dart:core';
import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
void main() async {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of the application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo Company',
theme: ThemeData(),
debugShowCheckedModeBanner: false,
home: const HomePage(title: 'Demo Company'),
);
}
}
class _HomePageState extends State<HomePage> {
#override
initState() {
super.initState();
}
Widget _infoTile(String title, String subtitle) {
return ListTile(
title: Text(title),
subtitle: Text(subtitle.isEmpty ? 'Not set' : subtitle),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: const NavDrawer(),
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
children: <Widget>[
_infoTile('App name', 'Demo App....'),
// Multiple Liste Tiles...
],
),
),
);
}
}
//----------------------------------------------------------------------
// navigation.dart
//----------------------------------------------------------------------
class NavDrawer extends StatelessWidget {
const NavDrawer({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const <Widget>[
Text(
'Navigation',
style: TextStyle(color: Colors.white, fontSize: 30),
),
SizedBox(height: 30.0),
Text('Firstname', style: TextStyle(color: Colors.black, fontSize: 15)),
Text('Accountname', style: TextStyle(color: Colors.black, fontSize: 15)),
],
),
),
ListTile(
leading: const Icon(Icons.notifications),
title: const Text('Demo'),
onTap: () {
Navigator.push(
context,
Demo.route(),
);
},
),
// Multiple Navigation List Tiles...
],
),
);
}
}
//----------------------------------------------------------------------
// demo.dart
//----------------------------------------------------------------------
class HomePage extends StatefulWidget {
const HomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<HomePage> createState() => _HomePageState();
}
class Demo extends StatefulWidget {
const Demo({Key? key}) : super(key: key);
static Route route() {
return CupertinoPageRoute(builder: (_) => const Demo());
}
#override
_DemoState createState() => _DemoState();
}
class _DemoState extends State<Demo> {
final _data = <WordPair>[];
#override
void initState() {
super.initState();
_data.addAll(generateWordPairs().take(20));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Woolha.com Flutter Tutorial'),
),
body: _buildList(),
);
}
Widget _buildList() {
return RefreshIndicator(
onRefresh: _refreshData,
child: ListView.builder(
padding: const EdgeInsets.all(20.0),
itemBuilder: (context, index) {
WordPair wordPair = _data[index];
return _buildListItem(wordPair.asString, context);
},
itemCount: _data.length,
),
);
}
Widget _buildListItem(String word, BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(word),
),
);
}
Future _refreshData() async {
await Future.delayed(const Duration(seconds: 3));
_data.clear();
_data.addAll(generateWordPairs().take(20));
setState(() {});
}
}
class ShowMessages extends StatelessWidget {
final String type;
final Color color;
const ShowMessages({Key? key, required this.type, required this.color}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView(
//color: color,
physics: const AlwaysScrollableScrollPhysics(),
children: [
ListTile(
title: Text(
type,
style: Theme.of(context).textTheme.bodyText1,
),
),
]);
}
}
Copy this code to DartPad
What is wrong?
Well for me this code... works
I copied it into Dartpad, then Dev Tools in browser (F12) > Device Emulation > Responsive. And you can use pull to refresh.
Of course this doesn't work using web view and mouse. I believe this gesture is not supported.

Flutter TextField value disappear when clicked on another textField

Am New in Flutter, Now am trying to add ModalBottomSheet with some textfields and whenever i try to tap on another textField then value got disappeared automatically.
Here is my new_user.dart file
import 'package:flutter/material.dart';
class newUsers extends StatelessWidget {
var nameInputController = TextEditingController();
var marksInputController = TextEditingController();
Function? addNewUser;
newUsers(this.addNewUser);
void submitData() {
print(nameInputController.text);
if (nameInputController.text.isEmpty || marksInputController.text.isEmpty) {
return;
}
addNewUser!(
nameInputController.text, double.parse(marksInputController.text));
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
TextField(
decoration: InputDecoration(labelText: "Name"),
controller: nameInputController,
),
TextField(
decoration: InputDecoration(labelText: "Marks"),
controller: marksInputController,
keyboardType: TextInputType.number,
),
ElevatedButton(
onPressed: submitData,
child: Text('Add User Details'),
),
],
),
);
}
}
Home.dart
import 'package:expense_manager/widgets/new_users.dart';
import 'package:expense_manager/widgets/users.dart';
import 'package:expense_manager/widgets/users_list.dart';
import 'models/users.dart';
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<User> users = [
User(id: 1, name: 'himanshurahi', marks: 44.5, createdAt: DateTime.now()),
];
void addNewUser(String name, double marks) {
var newUser =
User(id: 1, name: name, marks: marks, createdAt: DateTime.now());
setState(() {
users.add(newUser);
});
}
#override
Widget build(BuildContext context) {
void addNewUserWithSheet(BuildContext context) {
showModalBottomSheet(
context: context,
builder: (btx) {
return newUsers(addNewUser);
},
);
}
return Scaffold(
appBar: AppBar(
title: Text('my App'),
actions: [
IconButton(
onPressed: () => addNewUserWithSheet(context),
icon: Icon(Icons.add))
],
),
floatingActionButton: FloatingActionButton(
onPressed: () => addNewUserWithSheet(context),
child: Icon(Icons.add)),
body: Container(
child: Column(
children: [
Container(
width: double.infinity,
height: 100,
child: Card(
color: Colors.blueAccent,
child: Text("Chart"),
),
),
newUsers(addNewUser),
usersList(users)
],
),
));
}
}
Here i have added addNewUserWithSheet method to add bottom sheet.
here is main.dart file (if need)
import 'package:expense_manager/home.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Home(),
);
}
}
Thanx in advance :)
Ahhh I Basically solved it by using Stateful Widget.