Do someone know how to add dynamically more rows into the DataTable in Flutter. As you can see my code is very 'hardcoded' [line: 11-31].
There should be a way to get rid of writing more and more DataRows.
Code:
class DataTableWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DataTable(
columns: [
DataColumn(label: Text('Patch')),
DataColumn(label: Text('Version')),
DataColumn(label: Text('Ready')),
],
rows: <DataRow>[
DataRow(
cells: <DataCell>[
DataCell(Text('AAAAAA')),
DataCell(Text('1')),
DataCell(Text('Yes')),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('BBBBBB')),
DataCell(Text('2')),
DataCell(Text('No')),
],
),
DataRow(
cells: <DataCell>[
DataCell(Text('CCCCCC')),
DataCell(Text('3')),
DataCell(Text('Yes')),
],
),
],
);
}
}
You can use
listOfColumns.map(((element) => DataRow(...))).toList()
This is your code using this method.
class DataTableWidget extends StatelessWidget {
final List<Map<String, String>> listOfColumns = [
{"Name": "AAAAAA", "Number": "1", "State": "Yes"},
{"Name": "BBBBBB", "Number": "2", "State": "no"},
{"Name": "CCCCCC", "Number": "3", "State": "Yes"}
];
// DataTableWidget(this.listOfColumns); // Getting the data from outside, on initialization
#override
Widget build(BuildContext context) {
return DataTable(
columns: [
DataColumn(label: Text('Patch')),
DataColumn(label: Text('Version')),
DataColumn(label: Text('Ready')),
],
rows:
listOfColumns // Loops through dataColumnText, each iteration assigning the value to element
.map(
((element) => DataRow(
cells: <DataCell>[
DataCell(Text(element["Name"])), //Extracting from Map element the value
DataCell(Text(element["Number"])),
DataCell(Text(element["State"])),
],
)),
)
.toList(),
);
}
}
You could do something like this:**
class DataTableWidget extends StatelessWidget {
List results=[] ;
intState((){
super.iniState();
this.getSale();
})
Future<String> getData () async {
var response = await http.get(
"$saleUrl/?format=json",
);
setState(() {
var dataConvertedToJson =
json.decode(utf8.decode(response.bodyBytes));
results = dataConvertedToJson['results'];
});
print('${results.length}');
return "successful";
}
DataRow _getDataRow(result) {
return DataRow(
cells: <DataCell>[
DataCell(Text(data["text1"])),
DataCell(Text(data["text2"])),
DataCell(Text(data["text3"])),
],
);
}
#override
Widget build(BuildContext context) {
return DataTable(
columns: [
DataColumn(label: Text('Patch')),
DataColumn(label: Text('Version')),
DataColumn(label: Text('Ready')),
],
rows: List.generate(
results.length, (index) => _getDataRow(results[index])),
);
}
}
The underlying data structure, probably a list, is what you want to worry about changing. UI elements like the table just observe. The setState() function notifies the UI to take a fresh look after you've updated your list.
Here's a working example hacked from a flutter doc example. Click the button to add rows.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Add Rows',
home: MyHomePage(title: 'Add Rows'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<DataRow> _rowList = [
DataRow(cells: <DataCell>[
DataCell(Text('AAAAAA')),
DataCell(Text('1')),
DataCell(Text('Yes')),
]),
];
void _addRow() {
// Built in Flutter Method.
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below.
_rowList.add(DataRow(cells: <DataCell>[
DataCell(Text('BBBBBB')),
DataCell(Text('2')),
DataCell(Text('No')),
]));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: DataTable(columns: [
DataColumn(label: Text('Patch')),
DataColumn(label: Text('Version')),
DataColumn(label: Text('Ready')),
], rows: _rowList),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: _addRow,
label: Text('Add Row'),
backgroundColor: Colors.green,
),
);
}
}
This builds for me with
flutter build web
then run with
flutter run -d chrome
Related
I spent so much time for this. I am using navigator key for routing. In android I have no problem but in IOS navigator key is coming null in runtime. I have no idea what is the problem. In Android there is no problem. Working very well. Any idea why is it happening?
main.dart
void main() async {
// sets up the global service locator
setupServices();
CompanyErrorHandler(
child: EasyLocalization(
supportedLocales: const [
Locale('en', 'US'),
],
path: 'assets/translations',
child: const GlobalProvider(child: Company()),
));
}
Company.dart
class Company extends StatelessWidget {
const Company({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final themeBloc = context.watch<ThemeBloc>();
final systemBrightness = context.watch<Brightness>();
return MaterialApp(
title: 'company',
debugShowCheckedModeBanner: false,
theme: themeBloc.state.lightCompanyTheme.themeData,
darkTheme: themeBloc.state.darkCompanyTheme.themeData,
themeMode: systemBrightness == Brightness.light
? ThemeMode.light
: ThemeMode.dark,
color: Colors.black,
localizationsDelegates: context.localizationDelegates,
locale: context.locale,
supportedLocales: context.supportedLocales,
navigatorKey: app<CompanyNavigator>().key,
onGenerateRoute: onGenerateRoute,
navigatorObservers: [
companyRouteObserver,
],
home: const SplashScreen(),
builder: (_, child) => _ContentBuilder(child: child),
);
}
}
Company Navigator class
final companyRouteObserver = RouteObserver<ModalRoute<dynamic>>();
class CompanyNavigator {
final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>();
NavigatorState get state => key.currentState!;
}
CurrentState coming null for IOS in here.
I'm getting a peculiar bug when using a ListWheelScrollView to display image widgets on iOS. It is contained in one page of a PageView, and it works fine until I minimize the app. If the app is resumed after entering the background and then I switch to the page that contains the scrollview (either by switching away and switching back after resuming or by switching away before minimizing and then switching back after resuming), the visible images fail to display and the output reads as follows:
════════ Exception caught by image resource service
════════════════════════════ The method 'toDouble' was called on null.
Receiver: null Tried calling: toDouble()
════════════════════════════════════════════════════════════════════════════════
Below is a simple example that demonstrates the problem:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'ListWheel Issue',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'ListWheelScrollview Bug'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _currentIndex = 0;
PageController _pageController;
List<Widget> get _tabs => [
ScrollScreen(),
Container(),
Container(),
];
#override
void initState() {
_pageController = PageController();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: PageView(
controller: _pageController,
children: _tabs,
)),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(
icon: Icon(Icons.refresh), title: Text('tab1')),
BottomNavigationBarItem(icon: Icon(Icons.movie), title: Text('tab2')),
BottomNavigationBarItem(
icon: Icon(Icons.search), title: Text('tab3')),
],
currentIndex: _currentIndex,
onTap: (i) {
setState(() {
_currentIndex = i;
_pageController.animateToPage(i,
curve: Curves.easeOut, duration: Duration(milliseconds: 200));
});
},
),
);
}
}
class ScrollScreen extends StatefulWidget {
#override
_ScrollScreenState createState() => _ScrollScreenState();
}
class _ScrollScreenState extends State<ScrollScreen> {
var _exImage = AssetImage('assets/images/no_image.png');
#override
Widget build(BuildContext context) {
List<AssetImage> _images = [
_exImage,
_exImage,
_exImage,
];
return Center(
child: ListWheelScrollView.useDelegate(
itemExtent: 300,
childDelegate: ListWheelChildLoopingListDelegate(
children: _images
.map((e) => Center(
child: Image(
image: e,
)))
.toList())),
);
}
}
Any help with this would be greatly appreciated.
I found out a solution.
#override
void didChangeDependencies() {
super.didChangeDependencies();
precacheImage(AssetImage('assets/icons/settings_icon.png'), context);
}
after precaching the images used in the ListWheelScrollView, the error no longer appears
Idk if flutter team will fix it in the future or not.
I figured out a bit more about the issue. It turns out null values were being passed as "minScrollExtent" and "maxScrollExtent" into the _getItemFromOffset function in the list_wheel_scroll_view.dart file, which were, in turn being passed into the _clipOffsetToScrollableRange function.
As a workaround, I ended up changing the return value of the _clipOffsetScrollableRange to the following:
return (_clipOffsetToScrollableRange(
offset,
minScrollExtent ?? -double.infinity,
maxScrollExtent ?? double.infinity) /
itemExtent)
.round();
This likely does not address the underlying issue, but has resulted in my app working properly.
I'm using the Flutter UserAccountsDrawerHeader widget to display the user's data but I could not figure out how to implement the onDetailsPressed() function to call the user details. Here is my code:
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return new Scaffold(
drawer: _buildDrawer(context),
appBar: _buildAppBar(),
);
}
}
Widget _buildAppBar() {
return new AppBar();
}
Widget _buildDrawer(BuildContext context) {
return new Drawer(
child: new ListView(
children: <Widget>[
new UserAccountsDrawerHeader(
accountName: new Text("Cleudice Santos"),
accountEmail: new Text("cleudice.ms#gmail.com"),
onDetailsPressed: () {},
),
new ListTile(
title: new Text("Visão geral"),
leading: new Icon(Icons.dashboard),
onTap: () {
print("Visão geral");
},
),
],
),
);
}
I want to click the arrow and show the account details as shown below. That is, overlapping the content of the drawer. As the Gmail app does.
Basically, what you should be doing is replacing the rest of the content with user details rather than the current list. The simplest way to do this is to make your drawer into a stateful widget and have a boolean that keeps track of whether user details or the normal list should be shown.
I've added that to your code (and added a bit to make it self-contained so you can paste it to a new file to test out):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: UserDetailDrawer(),
appBar: _buildAppBar(),
);
}
}
Widget _buildAppBar() {
return AppBar();
}
class UserDetailDrawer extends StatefulWidget {
#override
_UserDetailDrawerState createState() => _UserDetailDrawerState();
}
class _UserDetailDrawerState extends State<UserDetailDrawer> {
bool showUserDetails = false;
Widget _buildDrawerList() {
return ListView(
children: <Widget>[
ListTile(
title: Text("Visão geral"),
leading: Icon(Icons.dashboard),
onTap: () {
print("Visão geral");
},
),
ListTile(
title: Text("Another tile??"),
leading: Icon(Icons.question_answer),
),
],
);
}
Widget _buildUserDetail() {
return Container(
color: Colors.lightBlue,
child: ListView(
children: [
ListTile(
title: Text("User details"),
leading: Icon(Icons.info_outline),
)
],
),
);
}
#override
Widget build(BuildContext context) {
return Drawer(
child: Column(children: [
UserAccountsDrawerHeader(
accountName: Text("Cleudice Santos"),
accountEmail: Text("cleudice.ms#gmail.com"),
onDetailsPressed: () {
setState(() {
showUserDetails = !showUserDetails;
});
},
),
Expanded(child: showUserDetails ? _buildUserDetail() : _buildDrawerList())
]),
);
}
}
I am learning flutter and I am working with tabBars and I am having an issue with saving the state. I have put a small working example of my issue below. Basically, there is a button and a stateful counter. When I click the button, I see the text field update correctly. But, when I switch to a different tab and come back, the text field is back to zero.
I have found if i move the following line outside of _CounterState so its defined at the top level of the file, then, it works correctly. When I switch tabs, the counter stays at the correct count when I switch back
int _counter = 0;
I don't feel like this is the appropriate way to do this and all of the examples I have seen have the variable inside of the class. Can anyone give me any insights? Why would it reset if it is inside the class? Am I supposed to keep it outside the class? Below is the simplified full example.
import 'package:flutter/material.dart';
void main() {
runApp(new TabBarDemo());
}
class TabBarDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new DefaultTabController(
length: 3,
child: new Scaffold(
appBar: new AppBar(
bottom: new TabBar(
tabs: [
new Tab(icon: new Icon(Icons.directions_car)),
new Tab(icon: new Icon(Icons.directions_transit)),
new Tab(icon: new Icon(Icons.directions_bike)),
],
),
title: new Text('Tabs Demo'),
),
body: new TabBarView(
children: [
new Counter(),
new Icon(Icons.directions_transit),
new Icon(Icons.directions_bike),
],
),
),
),
);
}
}
class Counter extends StatefulWidget {
#override
_CounterState createState() => new _CounterState();
}
class _CounterState extends State<Counter> {
int _counter = 0;
void _increment() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return new Row(
children: <Widget>[
new RaisedButton(
onPressed: _increment,
child: new Text('Increment'),
),
new Text('Count: $_counter'),
],
);
}
}
Below is the example with the counter moved outside of the class
import 'package:flutter/material.dart';
void main() {
runApp(new TabBarDemo());
}
class TabBarDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new DefaultTabController(
length: 3,
child: new Scaffold(
appBar: new AppBar(
bottom: new TabBar(
tabs: [
new Tab(icon: new Icon(Icons.directions_car)),
new Tab(icon: new Icon(Icons.directions_transit)),
new Tab(icon: new Icon(Icons.directions_bike)),
],
),
title: new Text('Tabs Demo'),
),
body: new TabBarView(
children: [
new Counter(),
new Icon(Icons.directions_transit),
new Icon(Icons.directions_bike),
],
),
),
),
);
}
}
class Counter extends StatefulWidget {
#override
_CounterState createState() => new _CounterState();
}
int _counter = 0; //<-- MOVED OUTSIDE THE _CounterState CLASS
class _CounterState extends State<Counter> {
void _increment() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return new Row(
children: <Widget>[
new RaisedButton(
onPressed: _increment,
child: new Text('Increment'),
),
new Text('Count: $_counter'),
],
);
}
}
As _CounterState widget is built everytime you go to the given TabView you'll need to put _counter variable in the state configuration class (Counter).
class Counter extends StatefulWidget {
int _counter = 0;
#override
_CounterState createState() => new _CounterState();
}
class _CounterState extends State<Counter> {
void _increment() {
setState(() {
widget._counter++;
});
}
#override
Widget build(BuildContext context) {
return new Row(
children: <Widget>[
new RaisedButton(
onPressed: _increment,
child: new Text('Increment'),
),
new Text('Count: ${widget._counter}'),
],
);
}
}
As I used one solution AutomaticKeepAliveClientMixin
You need to use this mixin with your state class of StateFullWidget.
you need to pass true to wantKeepAlive getter method.
class SampleWidget extends StatefulWidget {
#override
_SampleWidgetState createState() => _SampleWidgetState();
}
class _SampleWidgetState extends State<SampleWidget> with AutomaticKeepAliveClientMixin{
#override
Widget build(BuildContext context) {
super.build(context);
return Container();
}
#override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;
}
This will save your state and stop your widget to recreate again. I have used it with Tabbar and PageView and it's working fine.
put the variable in that statefulwidget and then call it every time as "widget.variable_name"
I'm making one demo for learning purpose. In that demo I have just added ListView and when I tap on the ListTile there is Icons.favorite I just want to do Active and Deactive.
Below are my code.
import 'package:flutter/material.dart';
void main() => runApp(new RandomWordApp());
class RandomWordApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return new MaterialApp(
title: "Random App",
home: new RandomWordHome(),
);
}
}
class RandomWordHome extends StatefulWidget {
#override
createState() => new RandomState();
}
class RandomState extends State<RandomWordHome> {
#override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text("Random Word"),
),
body: setupListView(),
);
}
Widget setupListView() {
final _arrayOfData = [
{
"name": "Amit Patel",
"address": "Junagadh"
},
{"name": "Arjun Jain", "address": "Madhya Pradesh"},
{"name": "Ajay Shah", "address": "Limbadi"},
{"name": "Ankur Patel", "address": "Visanagar"},
{"name": "Soheb Ansari", "address": "Ahmedabad"}
];
final _arraySelectedRowStatus = List.filled(5, 0);
return new ListView.builder(
padding: new EdgeInsets.all(10.0),
itemCount: _arrayOfData.length,
itemBuilder: (context, i) {
return new ListTile(
title: new Text(_arrayOfData[i]["name"]),
subtitle: new Text(_arrayOfData[i]["address"]),
leading: new CircleAvatar(
backgroundColor: Colors.blue,
child: new Text(_arrayOfData[i]["name"][0])),
trailing: new Icon(
(_arraySelectedRowStatus[i] == 0
? Icons.favorite_border
: Icons.favorite),
color: (_arraySelectedRowStatus[i] == 0 ? null : Colors.red),
),
onTap: () {
setState(
() {
_arraySelectedRowStatus[i] =
(_arraySelectedRowStatus[i] == 0 ? 1 : 0);
Scaffold.of(context).showSnackBar(new SnackBar(
content:
new Text("You are selecting at " + i.toString())));
},
);
},
);
});
}
}
All are working well, even I got expected output from below line
print(_arrayOfData[i]["name"] + " - " + _arraySelectedRowStatus[i].toString());
But Icon is not change might be because ListView is not reloading. Where I'm going wrong? Guide me on right direction.
There are some issues with your approach
In order to update your UI, you have to use setState() method. Changing the variable is not enough.
You always create a final List inside ListView.builder block: final _arraySelectedRowStatus = List.filled(5, 0); and then use this list to render your UI. So the _arraySelectedRowStatus will always contain 0.
Below is updated code which will work
import 'package:flutter/material.dart';
void main() => runApp(new RandomWordApp());
class RandomWordApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return new MaterialApp(
title: "Random App",
home: new RandomWordHome(),
);
}
}
class RandomWordHome extends StatefulWidget {
#override
createState() => new RandomState();
}
class RandomState extends State<RandomWordHome> {
List _arraySelectedRowStatus = List.filled(5, 0);
#override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text("Random Word"),
),
body: setupListView(),
);
}
Widget setupListView() {
final _arrayOfData = [
{"name": "Amit Patel", "address": "Junagadh"},
{"name": "Arjun Jain", "address": "Madhya Pradesh"},
{"name": "Ajay Shah", "address": "Limbadi"},
{"name": "Ankur Patel", "address": "Visanagar"},
{"name": "Soheb Ansari", "address": "Ahmedabad"}
];
return new ListView.builder(
padding: new EdgeInsets.all(10.0),
itemCount: _arrayOfData.length,
itemBuilder: (context, i) {
return new ListTile(
title: new Text(_arrayOfData[i]["name"]),
subtitle: new Text(_arrayOfData[i]["address"]),
leading: new CircleAvatar(
backgroundColor: Colors.blue,
child: new Text(_arrayOfData[i]["name"][0])),
trailing: new Icon(
(_arraySelectedRowStatus[i] == 0
? Icons.favorite_border
: Icons.favorite),
color: (_arraySelectedRowStatus[i] == 0 ? null : Colors.red),
),
onTap: () {
_arraySelectedRowStatus[i] =
(_arraySelectedRowStatus[i] == 0 ? 1 : 0);
print(_arrayOfData[i]["name"] +
" - " +
_arraySelectedRowStatus[i].toString());
setState(() {
_arraySelectedRowStatus = _arraySelectedRowStatus;
});
Scaffold.of(context).showSnackBar(new SnackBar(
content: new Text("You are selecting at " + i.toString())));
},
);
});
}
}
[Update]
Here is the complete working code.
class DemoHelp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
title: "Random App",
home: RandomWorldHome(),
);
}
}
class RandomWorldHome extends StatefulWidget {
#override
_RandomWorldHomeState createState() => new _RandomWorldHomeState();
}
class _RandomWorldHomeState extends State<RandomWorldHome> {
List<int> _arraySelectedRowStatus = List.filled(5, 1);
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("Random Word"),
),
body: setupListView(),
);
}
Widget setupListView() {
final _arrayOfData = [
{"name": "Amit Patel", "address": "Junagadh"},
{"name": "Arjun Jain", "address": "Madhya Pradesh"},
{"name": "Ajay Shah", "address": "Limbadi"},
{"name": "Ankur Patel", "address": "Visanagar"},
{"name": "Soheb Ansari", "address": "Ahmedabad"}
];
return ListView.builder(
padding: new EdgeInsets.all(10.0),
itemCount: _arrayOfData.length,
itemBuilder: (context, i) {
return ListTile(
title: Text(_arrayOfData[i]["name"]),
subtitle: new Text(_arrayOfData[i]["address"]),
leading: new CircleAvatar(
backgroundColor: Colors.blue,
child: new Text(_arrayOfData[i]["name"][0])),
trailing: new Icon(
(_arraySelectedRowStatus[i] == 0
? Icons.favorite_border
: Icons.favorite),
color: (_arraySelectedRowStatus[i] == 0 ? null : Colors.red),
),
onTap: (){
setState(() {
_arraySelectedRowStatus[i] = _arraySelectedRowStatus[i] == 0 ? 1 : 0;
});
Scaffold.of(context).showSnackBar(new SnackBar(
content:
new Text("You are selecting at ${_arraySelectedRowStatus[i]}")));
},
);
},
);
}
}
Couple of problems in your code.
variable _arraySelectedRowStatus is wrongly defined as final
state variable should be declared in the state class rather than in method.
setState((){}) is used like this.