I've some trouble when I try to save the reorderable grid after I've changed it.
It seem to work good but when i open again the app is only a grey screen.I've added some snackbar that print the list before and after been saved with GetStorage but they are in the right order as you can see in this screenshot
imagePaths before being saved
imagePaths loaded from GetStorage
grey screen when I reopening the app
import 'package:flutter/material.dart';
import 'package:reorderable_grid_view/reorderable_grid_view.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
void main() async {
await GetStorage.init();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final box = GetStorage();
List<String> imagePaths = GetStorage().read('imagePaths') ?? [
'remote',
'timelapse',
'video',
'hdr',
'star',
'lightning',
'drop',
'vibrate',
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: ReorderableGridView.count(
crossAxisCount: 2,
childAspectRatio: 1,
children: imagePaths
.map((String path) => Card(
key: ValueKey(path),
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(path),
SvgPicture.asset(
'assets/$path.svg',
color: Colors.red,
),
],
),
),
))
.toList(),
onReorder: (oldIndex, newIndex) async {
String path = imagePaths.removeAt(oldIndex);
imagePaths.insert(newIndex, path);
setState(() {
Get.snackbar(
'before GetStorage:',
'${imagePaths.toString()}',
snackPosition: SnackPosition.BOTTOM,
);
box.remove('imagePaths');
box.write('imagePaths', imagePaths);
Get.snackbar(
'from GetStorage:',
'${box.read('imagePaths').toString()}',
snackPosition: SnackPosition.BOTTOM,
);
});
},
),
);
}
}
I solved your problem like here:
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List imagePaths = [
'remote',
'timelapse',
'video',
'hdr',
'star',
'lightning',
'drop',
'vibrate'
];
final box = GetStorage();
final String key = 'imagePaths';
#override
void initState() {
imagePaths = box.read(key);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ReorderableGridView.count(
crossAxisCount: 2,
childAspectRatio: 1,
children: imagePaths
.map((path) => Card(
key: ValueKey(path),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(path),
SvgPicture.asset(
'assets/$path.svg',
color: Colors.red,
),
],
),
))
.toList(),
onReorder: (oldIndex, newIndex) async {
String path = imagePaths.removeAt(oldIndex);
imagePaths.insert(newIndex, path);
setState(() {
box.remove(key);
box.write(key, imagePaths);
});
},
),
);
}
}
Related
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:
I have a list of integers. Each of this item is displayed in a statefull widget by iterating the list in the build method.
import 'package:flutter/material.dart';
import 'package:widget_list/ItemWidget.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: 'Item list state demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Item list state demo'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static int itemsCount = 0;
final List<int> _items = List.empty(growable: true);
void _add() {
setState(() {
_items.add(itemsCount++);
});
}
void _remove() {
setState(() {
_items.removeAt(0);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
children: [
TextButton(
onPressed: () => _add(),
child: const Text('Add item'),
),
TextButton(
onPressed: () => _items.isNotEmpty ? _remove() : null,
child: const Text('Remove item'),
),
],
),
for (var item in _items) ItemWidget(item: item),
],
),
),
);
}
}
Each of this widget, has a statically incremented integer "id" in it's state. Both the item and the widget id are displayed.
import 'package:flutter/material.dart';
var widgetCount = 0;
class ItemWidget extends StatefulWidget {
final int item;
const ItemWidget({
required this.item,
Key? key,
}) : super(key: key);
#override
State<ItemWidget> createState() => _ItemWidgetState();
}
class _ItemWidgetState extends State<ItemWidget> {
final int widgetId = widgetCount++;
#override
Widget build(BuildContext context) {
print("Item ${widget.item} / Widget $widgetId");
return Text("Item ${widget.item} / Widget $widgetId");
}
}
When I add an item in the list, it is displayed in a newly generated widget. E.g. first item 0 is displayed in widget 0.
But if I remove an item at the beginning of the list (e.g. item 0), it's not the first widget that is destoyed, but the last one. The item 1 is then displayed in widget 0.
The widget item is final, so it cannot change. The widget ids are still the same, so the states were not rebuild. Then, why are the states no more consistent with the widgets?
This is done in FLutter desktop for Linux, v3.0.1
In the itemWidget you are creating a value from 0 so for each element that is rendered it will start from 0. please check the code below
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Item list state demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Item list state demo'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static int itemsCount = 0;
final List<ItemInfo> _items = List.empty(growable: true);
void _add() {
setState(() {
itemsCount++;
_items.add(ItemInfo(itemsCount, itemsCount));
});
}
void _remove() {
setState(() {
_items.removeAt(0);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
children: [
TextButton(
onPressed: () => _add(),
child: const Text('Add item'),
),
TextButton(
onPressed: () => _items.isNotEmpty ? _remove() : null,
child: const Text('Remove item'),
),
],
),
for (var item in _items) ItemWidget(item: item),
],
),
),
);
}
}
and Itemwidget to be like this
class ItemWidget extends StatefulWidget {
final ItemInfo item;
const ItemWidget({
required this.item,
Key? key,
}) : super(key: key);
#override
State<ItemWidget> createState() => _ItemWidgetState();
}
class _ItemWidgetState extends State<ItemWidget> {
#override
Widget build(BuildContext context) {
return Text(
"Item ${widget.item.itemVal} / Widget ${widget.item.itemIndex}");
}
}
also I created a class named ItemInfo which will hold both the value and its index.
class ItemInfo {
int itemVal;
int itemIndex;
ItemInfo(this.itemVal, this.itemIndex);
}
I'm trying to make simple echo WebSocket in Dart with remote server, but it's not work (I don't get echo messages back to me). There is no compile errors or logs. No issues with Flutter Doctor. Rebuild doesn't help.
I could find only outdated examples for localhost and not for server.
Both machines are on the same network and can see each other.
server app code:
import 'dart:io';
void main() async {
HttpServer server = await HttpServer.bind('localhost', 8082);
server.transform(WebSocketTransformer()).listen(onWebSocketData);
}
void onWebSocketData(WebSocket client){
client.listen((data) {
client.add('Echo: $data');
});
}
echo app code:
import 'package:web_socket_channel/web_socket_channel.dart';
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) {
const title = 'WebSocket Demo';
return const MaterialApp(
title: title,
home: MyHomePage(
title: title,
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({
Key? key,
required this.title,
}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final TextEditingController _controller = TextEditingController();
final _channel = WebSocketChannel.connect(
Uri.parse('wss://172.22.185.10:8082'),
);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Form(
child: TextFormField(
controller: _controller,
decoration: const InputDecoration(labelText: 'Send a message'),
),
),
const SizedBox(height: 24),
StreamBuilder(
stream: _channel.stream,
builder: (context, snapshot) {
return Text(snapshot.hasData ? '${snapshot.data}' : '');
},
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _sendMessage,
tooltip: 'Send message',
child: const Icon(Icons.send),
),
);
}
void _sendMessage() {
if (_controller.text.isNotEmpty) {
_channel.sink.add(_controller.text);
}
}
#override
void dispose() {
_channel.sink.close();
_controller.dispose();
super.dispose();
}
}
I was not able to find any detailed documentation or up-to-date answers, so...
Please help me if you can <3
For server shelf package used
shelf_web_socket: ^1.0.1
Client is mentioned in flutter documentation
Client.dart
import 'package:web_socket_channel/web_socket_channel.dart';
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) {
const title = 'WebSocket Demo';
return const MaterialApp(
title: title,
home: MyHomePage(
title: title,
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({
Key? key,
required this.title,
}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final TextEditingController _controller = TextEditingController();
final _channel = WebSocketChannel.connect(
Uri.parse('ws://localhost:9001'),
);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Form(
child: TextFormField(
controller: _controller,
decoration: const InputDecoration(labelText: 'Send a message'),
),
),
const SizedBox(height: 24),
StreamBuilder(
stream: _channel.stream,
builder: (context, snapshot) {
return Text(snapshot.hasData ? '${snapshot.data}' : '');
},
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _sendMessage,
tooltip: 'Send message',
child: const Icon(Icons.send),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
void _sendMessage() {
if (_controller.text.isNotEmpty) {
_channel.sink.add(_controller.text);
}
}
#override
void dispose() {
_channel.sink.close();
_controller.dispose();
super.dispose();
}
}
Server.dart
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_web_socket/shelf_web_socket.dart';
void main() {
var handler = webSocketHandler((webSocket) {
webSocket.stream.listen((message) {
webSocket.sink.add("echo $message");
});
});
shelf_io.serve(handler, 'localhost', 9001).then((server) {
print('Serving at ws://${server.address.host}:${server.port}');
});
}
Remote Accessing
Remote accessing not a complicated
in your server side has a firewall . so you need to allow connection to this port( here 9001) for outside acces.Set your server ip in the code instead of localhost in both side like client and server.
Allow port in firewall on windows Os
1
2
3
4
5
6
7
Done.now you can access the port outside.
I am passing a call back function from my child file to the parent file, in the elevated button class, I am passing the function in the class (in the child file )to the named parameter on pressed, but I am getting the error in the question above, while it worked for the tutorial I am using(academind).
How do I solve this?
CHILD file
class Answer extends StatelessWidget {
final Function selectHandler;
Answer(this.selectHandler);
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
child: ElevatedButton(
child: Text('Answer 1'),
style: ElevatedButton.styleFrom(
primary: Colors.lightGreen
),
onPressed: selectHandler,
),
);
}
}
PARENT file
// ignore_for_file: prefer_const_constructors, avoid_print, prefer_const_literals_to_create_immutables, must_be_immutable, prefer_const_constructors_in_immutables
import 'package:flutter/material.dart';
import './question.dart';
import './answer.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() {
// ignore: todo
// TODO: implement createState
return _MyAppState();
}
}
class _MyAppState extends State <MyApp>{
var _questionsIndex = 0;
var questions = [
'What\'s your favourite book',
'What\'s your favourite colour',
'What\'s our favourite poem',
];
void _answerQuestion() {
setState(() {
_questionsIndex = _questionsIndex + 1;
if (_questionsIndex >= questions.length) _questionsIndex = 0;
});
print(_questionsIndex);
print(questions.length);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('My First App'),),
body: Column(
children: [
Question(
questions[_questionsIndex],
),
Answer(_answerQuestion),
Answer(_answerQuestion),
Answer(_answerQuestion),
],
)
),
);
}
}
Change Function to
void Function functionName(){}
or
VoidCallback functionName(){}
Same issue i have faced after updating this it is working
import 'package:flutter/material.dart';
class Answer extends StatelessWidget {
final VoidCallback clickFunction;
const Answer(this.clickFunction,{Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(5),
child: RaisedButton(
padding: const EdgeInsets.all(10),
color: Colors.blue,
onPressed: clickFunction,
child: const Text("This is texxt"),
));
}
}
this is worked for me
final void Function()? FunctionName;
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