error when creating glm for gtsummary table - linear-regression

I am trying to build a logistic regression using the code below, but I encounter the error that I am unable to identify the cause of! I have compared the contents of my df to the example used in a recent gtsummary seminar (https://cran.r-project.org/web/packages/gtsummary/vignettes/tbl_regression.html) and I can't find the cause.
Error in glm(disease_present ~ disease_cat, sex, age, data = df, :
'weights' must be a numeric vector
Code
mod <- glm(disease_present ~ disease_cat, sex, age,
data = df, family = binomial)
Data
df <-
structure(
list(
disease_cat = structure(
c(
7L,
2L,
2L,
2L,
2L,
5L,
2L,
2L,
2L,
2L,
2L,
3L,
2L,
2L,
5L,
2L,
2L,
2L,
2L,
2L,
2L,
2L,
5L,
5L,
5L,
2L,
8L,
2L,
8L,
2L,
7L,
2L,
7L,
2L,
7L,
8L,
8L,
2L,
2L,
7L,
2L,
7L,
7L,
2L,
2L,
2L,
2L,
2L,
5L,
5L
),
.Label = c(
"breast",
"prostate",
"colorectal",
"lung",
"melanoma",
"bladder",
"pancreatic",
"endometrial"
),
class = "factor"
),
disease_present = c(
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
0,
0,
0,
1,
1,
1,
0,
1,
0,
1,
0,
1,
0,
1,
0,
1,
1,
1,
0,
0,
1,
0,
1,
1,
0,
0,
0,
0,
0,
1,
1
),
sex = structure(
c(
2L,
2L,
1L,
2L,
1L,
2L,
1L,
2L,
1L,
1L,
2L,
1L,
1L,
1L,
2L,
2L,
1L,
2L,
2L,
1L,
2L,
2L,
1L,
1L,
2L,
2L,
2L,
1L,
1L,
1L,
1L,
1L,
1L,
1L,
2L,
2L,
1L,
1L,
2L,
1L,
2L,
1L,
1L,
2L,
1L,
1L,
2L,
2L,
1L,
1L
),
.Label = c("female", "male"),
class = "factor"
),
age = c(
18,
41,
40,
45,
48,
25,
50,
56,
45,
46,
45,
28,
40,
50,
25,
50,
40,
40,
40,
40,
50,
50,
29,
26,
8,
55,
60,
40,
35,
40,
22,
45,
10,
45,
14,
21,
45,
53,
55,
18,
45,
18,
35,
53,
45,
45,
54,
48,
30,
44
)
),
row.names = c(NA,-50L),
class = c("tbl_df",
"tbl", "data.frame")
)

Related

How to create two dynamic SfCartesianChart in Flutter?

I am trying to create multiple dynamic charts (SfCartesianChart from syncfusion), the data is extracted every 1 second from a dynamic json file.
I tried to create two charts; the first one works fine but the second one remains the same , it does not change.
This is what I am getting.
Here is my code
import 'dart:ui';
import 'dart:async';
import 'package:syncfusion_flutter_gauges/gauges.dart';
import 'package:flutter/material.dart';
import 'package:applicationv1/constants.dart';
import 'package:http/http.dart' as http;
import 'package:syncfusion_flutter_charts/charts.dart';
import 'package:applicationv1/classDonnee.dart';
import 'dart:convert';
Future<Donnee> fetchDonnee() async {
print('fetch0');
final response = await http.get(Uri.parse('uri'));
if (response.statusCode == 200) {
print('fecth1');
// If the server did return a 200 OK response, then parse the JSON.
return Donnee.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response, then throw an exception.
throw Exception('Failed to load album');
}
}
class depart1 extends StatefulWidget{
const depart1({Key? key}) : super(key: key);
#override
_depart1State createState() => _depart1State();
}
class _depart1State extends State<depart1> with SingleTickerProviderStateMixin{
late List<LiveData> chartData;
late List<LiveData2> chartData2;
ChartSeriesController? _chartSeriesController;
ChartSeriesController? _chartSeriesController2;
late Future<Donnee> futureDonnee;
Timer? timer;
#override
void initState() {
print('initstate');
futureDonnee=fetchDonnee();
chartData = getChartData();
chartData2 = getChartData2();
//super.initState();
timer= Timer.periodic(const Duration(seconds:1), (Timer t){
futureDonnee=fetchDonnee();
setState(() {
});
print('initstate1');
});
super.initState();
}
#override
Widget build(BuildContext context){
Size size = MediaQuery.of(context).size;
FutureBuilder f1;
//TabController _tabController;
print('build');
Center(child: f1=FutureBuilder<Donnee>(
future: futureDonnee,
builder: (context, snapshot) {
if (snapshot.hasData){
updateDataSource(snapshot.data!.w1);
updateDataSource2(snapshot.data!.va1)
return Center(
child:SingleChildScrollView(
child: Column(
children: <Widget>[
//chart 1
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white
),
margin: const EdgeInsets.fromLTRB(20,10,20,0),
padding: EdgeInsets.fromLTRB(2, 7, 2, 7),
//color: Colors.white,
child: Column(
children:[
Text('puissance Active de phase 1 (W)'),
SizedBox(height: 5,),
SfCartesianChart(
series: <LineSeries<LiveData, int>>[
LineSeries<LiveData, int>(
onRendererCreated: (ChartSeriesController controller) {
_chartSeriesController = controller;
},
dataSource: chartData,
color: Colors.blue,
xValueMapper: (LiveData sales, _) => sales.time,
yValueMapper: (LiveData sales, _) => sales.speed,
dataLabelSettings: DataLabelSettings(isVisible: true)
)
],
primaryXAxis: NumericAxis(
majorGridLines: const MajorGridLines(width: 0),
edgeLabelPlacement: EdgeLabelPlacement.shift,
interval: 3,
title: AxisTitle(text: 'Temps (secondes)')),
primaryYAxis: NumericAxis(
axisLine: const AxisLine(width: 0),
majorTickLines: const MajorTickLines(size: 0),
//title: AxisTitle(text: 'puissance Active de pahse 1 (W)')
)
),]
),),
////////////////////chart2
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white
),
margin: const EdgeInsets.fromLTRB(20,10,20,0),
padding: EdgeInsets.fromLTRB(2, 7, 2, 7),
child: Column(
children:[
Text('puissance Apparente de phase 1 (VA)'),
SizedBox(height: 5,),
SfCartesianChart(
series: <LineSeries<LiveData2, int>>[
LineSeries<LiveData2, int>(
onRendererCreated: (ChartSeriesController controller2) {
_chartSeriesController2 = controller2;
},
dataSource: chartData2,
color: Colors.yellow,
xValueMapper: (LiveData2 sales, _) => sales.time,
yValueMapper: (LiveData2 sales, _) => sales.speed,
dataLabelSettings: DataLabelSettings(isVisible: true)
)
],
primaryXAxis: NumericAxis(
majorGridLines: const MajorGridLines(width: 0),
edgeLabelPlacement: EdgeLabelPlacement.shift,
interval: 3,
title: AxisTitle(text: 'Temps (secondes)')),
primaryYAxis: NumericAxis(
axisLine: const AxisLine(width: 0),
majorTickLines: const MajorTickLines(size: 0),
//title: AxisTitle(text: 'puissance Active de pahse 1 (W)')
)
),]
),),
],
),
)
);
//);
}
else if (snapshot.hasError) {
//return const Text('Please wait');
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return Container(
height: 20,
width:20,
child: const CircularProgressIndicator());
},
)
);
return f1;
}
int time = 19;
void updateDataSource(String val) {
ChartSeriesController controller;
var snapshot;
chartData.add(LiveData(time++, double.parse(val)));
//math.Random().nextInt(60) + 30
chartData.removeAt(0);
_chartSeriesController?.updateDataSource(
addedDataIndex: chartData.length - 1, removedDataIndex: 0);
print('update chart');
}
List<LiveData> getChartData() {
return <LiveData>[
LiveData(0, 0),
LiveData(1, 0),
LiveData(2, 0),
LiveData(3, 0),
LiveData(4, 0),
LiveData(5, 0),
LiveData(6, 0),
LiveData(7, 0),
LiveData(8, 0),
LiveData(9, 0),
LiveData(10, 0),
LiveData(11, 0),
LiveData(12, 0),
LiveData(13, 0),
LiveData(14, 0),
LiveData(15, 0),
LiveData(16, 0),
LiveData(17, 0),
LiveData(18, 0)
];
}
//coube2
int time2 = 19;
void updateDataSource2(String val) {
ChartSeriesController controller2;
var snapshot;
chartData2.add(LiveData2(time2++, double.parse(val)));
chartData2.removeAt(0);
_chartSeriesController2?.updateDataSource(
addedDataIndex: chartData2.length - 1, removedDataIndex: 0);
print('update chart2');
}
List<LiveData2> getChartData2() {
return <LiveData2>[
LiveData2(0, 0),
LiveData2(1, 0),
LiveData2(2, 0),
LiveData2(3, 0),
LiveData2(4, 0),
LiveData2(5, 0),
LiveData2(6, 0),
LiveData2(7, 0),
LiveData2(8, 0),
LiveData2(9, 0),
LiveData2(10, 0),
LiveData2(11, 0),
LiveData2(12, 0),
LiveData2(13, 0),
LiveData2(14, 0),
LiveData2(15, 0),
LiveData2(16, 0),
LiveData2(17, 0),
LiveData2(18, 0)
];
}
}
class LiveData {
LiveData(this.time, this.speed);
final int time;
final num speed;
}
class LiveData2 {
LiveData2(this.time, this.speed);
final int time;
final num speed;
}
Any help is much appreciated
I tried to replicate your scenario with the provided code, but both the charts are not updating with your code. Instead of JSON data, we created data dynamically. And we found that, in the timer, you have called the setstate, and in the builder, you have called the updateDataSource method. You have processed setstate and updateDataSource simultaneously. If you comment on the updateDataaSource method, due to the setstate method you have called, the chart is getting updated. But for live update cases, we suggest using the updateDataSource method. In the timer, you can call the updateDataSource method, so that chart will get updated properly. And we are not sure why you have used both methods simultaneously. As of now, we have modified your code snippet by updating the random data at each time by calling the updateDataSource method and ensured that it updates the data properly and attached it below. You can modify the sample as per your requirement.
Code snippet:
import 'dart:math';
import 'dart:ui';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import 'package:syncfusion_flutter_charts/charts.dart';
import 'dart:convert';
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 Depart1(),
);
}
}
class Depart1 extends StatefulWidget {
const Depart1({Key? key}) : super(key: key);
#override
_Depart1State createState() => _Depart1State();
}
class _Depart1State extends State<Depart1> with SingleTickerProviderStateMixin {
late List<LiveData> chartData;
late List<LiveData2> chartData2;
ChartSeriesController? _chartSeriesController;
ChartSeriesController? _chartSeriesController2;
Timer? timer;
#override
void initState() {
chartData = getChartData();
chartData2 = getChartData2();
timer = Timer.periodic(const Duration(seconds: 1), (Timer t) {
updateDataSource('val');
updateDataSource2('val');
});
super.initState();
}
Future<String> getJsonData() async {
return await rootBundle.loadString('assets/data.json');
}
Future loadData() async {
String response = await getJsonData();
final dynamic responseData = json.decode(response);}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
FutureBuilder f1;
Center(
child: f1 = FutureBuilder(
future: getJsonData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Center(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white),
margin: const EdgeInsets.fromLTRB(20, 10, 20, 0),
padding: EdgeInsets.fromLTRB(2, 7, 2, 7),
child: Column(children: [
Text('puissance Active de phase 1 (W)'),
SizedBox(
height: 5,
),
SfCartesianChart(
series: <LineSeries<LiveData, DateTime>>[
LineSeries<LiveData, DateTime>(
onRendererCreated:
(ChartSeriesController controller) {
_chartSeriesController = controller;
},
dataSource: chartData,
color: Colors.blue,
xValueMapper: (LiveData sales, _) => sales.time,
yValueMapper: (LiveData sales, _) => sales.speed,
dataLabelSettings:
DataLabelSettings(isVisible: true))
],
primaryXAxis: DateTimeAxis(
majorGridLines: const MajorGridLines(width: 0),
edgeLabelPlacement: EdgeLabelPlacement.shift,
interval: 3,
title: AxisTitle(text: 'Temps (secondes)')),
primaryYAxis: NumericAxis(
axisLine: const AxisLine(width: 0),
majorTickLines: const MajorTickLines(size: 0),
)),
]),
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white),
margin: const EdgeInsets.fromLTRB(20, 10, 20, 0),
padding: EdgeInsets.fromLTRB(2, 7, 2, 7),
child: Column(children: [
Text('puissance Apparente de phase 1 (VA)'),
SizedBox(
height: 5,
),
SfCartesianChart(
series: <LineSeries<LiveData2, DateTime>>[
LineSeries<LiveData2, DateTime>(
onRendererCreated:
(ChartSeriesController controller2) {
_chartSeriesController2 = controller2;
},
dataSource: chartData2,
color: Colors.yellow,
xValueMapper: (LiveData2 sales, _) => sales.time,
yValueMapper: (LiveData2 sales, _) => sales.speed,
dataLabelSettings:
DataLabelSettings(isVisible: true))
],
primaryXAxis: DateTimeAxis(
majorGridLines: const MajorGridLines(width: 0),
edgeLabelPlacement: EdgeLabelPlacement.shift,
interval: 3,
title: AxisTitle(text: 'Temps (secondes)')),
primaryYAxis: NumericAxis(
axisLine: const AxisLine(width: 0),
majorTickLines: const MajorTickLines(size: 0),
)),
]),
),
],
),
));
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return Container(
height: 20, width: 20, child: const CircularProgressIndicator());
},
));
return Scaffold(appBar: AppBar(), body: f1);
}
int time = 19;
void updateDataSource(String val) {
ChartSeriesController controller;
var snapshot;
chartData.add(LiveData(chartData2.last.time.add(const Duration(days: 1)),
_getRandomInt(10, 100)));
chartData.removeAt(0);
_chartSeriesController?.updateDataSource(
addedDataIndex: chartData.length - 1, removedDataIndex: 0);
print('update chart');
}
List<LiveData> getChartData() {
return <LiveData>[
LiveData(DateTime(2022, 01, 00), 0),
LiveData(DateTime(2022, 01, 01), 0),
LiveData(DateTime(2022, 01, 02), 0),
LiveData(DateTime(2022, 01, 03), 0),
LiveData(DateTime(2022, 01, 04), 0),
LiveData(DateTime(2022, 01, 05), 0),
LiveData(DateTime(2022, 01, 06), 0),
LiveData(DateTime(2022, 01, 07), 0),
LiveData(DateTime(2022, 01, 08), 0),
LiveData(DateTime(2022, 01, 09), 0),
LiveData(DateTime(2022, 01, 10), 0),
LiveData(DateTime(2022, 01, 11), 0),
LiveData(DateTime(2022, 01, 12), 0),
LiveData(DateTime(2022, 01, 13), 0),
LiveData(DateTime(2022, 01, 14), 0),
LiveData(DateTime(2022, 01, 15), 0),
LiveData(DateTime(2022, 01, 16), 0),
LiveData(DateTime(2022, 01, 17), 0),
LiveData(DateTime(2022, 01, 18), 0)
];
}
int time2 = 19;
void updateDataSource2(String val) {
ChartSeriesController controller2;
var snapshot;
chartData2.add(LiveData2(chartData2.last.time.add(const Duration(days: 1)),
_getRandomInt(10, 100)));
chartData2.removeAt(0);
_chartSeriesController2?.updateDataSource(
addedDataIndex: chartData2.length - 1, removedDataIndex: 0);
}
List<LiveData2> getChartData2() {
return <LiveData2>[
LiveData2(DateTime(2022, 01, 31), 0),
LiveData2(DateTime(2022, 01, 01), 0),
LiveData2(DateTime(2022, 01, 02), 0),
LiveData2(DateTime(2022, 01, 03), 0),
LiveData2(DateTime(2022, 01, 04), 0),
LiveData2(DateTime(2022, 01, 05), 0),
LiveData2(DateTime(2022, 01, 06), 0),
LiveData2(DateTime(2022, 01, 07), 0),
LiveData2(DateTime(2022, 01, 08), 0),
LiveData2(DateTime(2022, 01, 09), 0),
LiveData2(DateTime(2022, 01, 10), 0),
LiveData2(DateTime(2022, 01, 11), 0),
LiveData2(DateTime(2022, 01, 12), 0),
LiveData2(DateTime(2022, 01, 13), 0),
LiveData2(DateTime(2022, 01, 14), 0),
LiveData2(DateTime(2022, 01, 15), 0),
LiveData2(DateTime(2022, 01, 16), 0),
LiveData2(DateTime(2022, 01, 17), 0),
LiveData2(DateTime(2022, 01, 18), 0)
];
}
int _getRandomInt(int min, int max) {
return Random().nextInt(max - min);
}
}
class LiveData {
LiveData(this.time, this.speed);
final DateTime time;
final num speed;
}
class LiveData2 {
LiveData2(this.time, this.speed);
final DateTime time;
final num speed;
}

File does not get converted to base64 string properly

So I have written some code to capture an image from the user using the better_camera package and then store it in a file. Then I convert it to base64 string for further use. It seems like the image is getting saved properly since Image.file shows the image. But when I convert the image to base64string, it gives the image of a small white screen. I do not know where the problem is coming. Can someone check?
CODE
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_better_camera/camera.dart';
import 'package:frappe/flutter_flow/flutter_flow_theme.dart';
import 'package:path_provider/path_provider.dart';
class AddNewCameraItem extends StatefulWidget {
const AddNewCameraItem({Key key}) : super(key: key);
#override
_AddNewCameraItemState createState() => _AddNewCameraItemState();
}
class _AddNewCameraItemState extends State<AddNewCameraItem> {
CameraController controller;
#override
void initState() {
super.initState();
initCam();
}
void initCam() async {
List<CameraDescription> cameras = await availableCameras();
controller = CameraController(cameras[0], ResolutionPreset.max);
controller.initialize().then((_) {
controller.setFlashMode(FlashMode.off);
if (!mounted) {
return;
}
setState(() {});
});
}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: FlutterFlowTheme.secondaryColor,
automaticallyImplyLeading: true,
centerTitle: true,
title: Text(
'ADD NEW ITEMS',
style: FlutterFlowTheme.bodyText1.override(
fontFamily: 'Noto Serif',
color: Color(0xFFFAFAFA),
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
elevation: 4,
),
body: !controller.value.isInitialized
? Container()
// : Image.file(File(
// '/data/user/0/com.flutterflow.frappe/cache/lkpbpyigaqpjsh6iygy8dA4dsFDG9CAcVD7DueDLtd3C3JqzmmRuZcKaFFmAt88Ryyp208CzlE1oYfkJKnvwErBFv3DkO0dSYLZApETWDnsGOur728R7WgoUQ1EEOv9ilz1jAeNLotSsYFaI13Jk9wp5mmGdqhmRx6LYSCfHQHAdalrMCtniqrU3Sqhaw1iJZfeuUsJy.jpg')),
: MaterialApp(
home: CameraPreview(controller),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
try {
Directory tempDir = await getTemporaryDirectory();
String randomName =
tempDir.path + '/' + getRandomString(200) + '.jpg';
// await controller.takePicture(randomName);
File file = File(
'/data/user/0/com.flutterflow.frappe/cache/IfXsEjNsSifPiQJ0d628j2d1HRh2Ww2Flin3urZc4d59WYeZ9DQ8mjpLdrt2jwzBjn2vxqvnkzRd4AGowI6fKCGRNWOfei7B2KufbpOG0RsNH7mobwGa7KS7q1C6ALoNIVJ13XLqpa7BX7pIQSgLobe5lW6z7P01QBms3CVrM9omeEvIZxRwxL5s555HLOGN4Te9Bta2.jpg');
Uint8List list = await file.readAsBytes();
print(list);
print(base64Encode(list.toList()));
print("Random n: " + randomName);
// print(base64Encode((await xfile.readAsBytes()).toList()));
} catch (e) {
print("Error: " + e.toString());
}
},
child: Center(
child: Icon(Icons.camera_alt, color: FlutterFlowTheme.tertiaryColor),
),
),
);
}
String getRandomString(int length) {
String _chars =
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
Random _rnd = Random();
return String.fromCharCodes(Iterable.generate(
length, (_) => _chars.codeUnitAt(_rnd.nextInt(_chars.length))));
}
}
BASE64OUTPUT
/9j/4QurRXhpZgAATU0AKgAAAAgACwEPAAIAAAAHAAAAkgEQAAIAAAARAAAAmgESAAMAAAABAAYAAAEaAAUAAAABAAAArAEbAAUAAAABAAAAtAEoAAMAAAABAAIAAAExAAIAAAA9AAAAvAEyAAIAAAAUAAAA+gITAAMAAAABAAEAAIdpAAQAAAABAAABDoglAAQAAAABAAAC8gAAAvhYaWFvbWkAAFJlZG1pIE5vdGUgNSBQcm8AAAAAAEgAAAABAAAASAAAAAF3aHlyZWQtdXNlciA5IFBLUTEuMTgwOTA0LjAwMSBWMTEuMC41LjAuUEVJTUlYTSByZWxlYXNlLWtleXMAADIwMjE6MDg6MTggMTk6MTY6MTkAAByCmgAFAAAAAQAAAmSCnQAFAAAAAQAAAmyIIgADAAAAAQAAAACIJwADAAAAAQJDAACQAAAHAAAABDAyMjCQAwACAAAAFAAAAnSQBAACAAAAFAAAAoiRAQAHAAAABAECAwCSAQAKAAAAAQAAApySAgAFAAAAAQAAAqSSAwAKAAAAAQAAAqySBwADAAAAAQABAACSCQADAAAAAQAQAACSCgAFAAAAAQAAArSSkAACAAAABwAAArySkQACAAAABwAAAsSSkgACAAAABwAAAsygAAAHAAAABDAxMDCgAQADAAAAAQABAACgAgAEAAAAAQAABpCgAwAEAAAAAQAABOigBQAEAAAAAQAAAtOiFwADAAAAAQACAACjAQAHAAAAAQEAAACkAgADAAAAAQAAAACkAwADAAAAAQAAAACkBQADAAAAAQAEAACkBgADAAAAAQAAAAAAAAAAAAAAAQAAAB4AAADcAAAAZDIwMjE6MDg6MTggMTk6MTY6MTkAMjAyMTowODoxOCAxOToxNjoxOQAAABMqAAAD6AAAAOMAAABkAAAAAAAAAGQAAA7iAAAD6Dk1NTM0OQBzOTU1MzQ5AAA5NTUzNDkAAAIAAQACAAAABFI5OAAAAgAHAAAABDAxMDAAAAAAAAA
UINT8LIST
[255, 216, 255, 225, 11, 171, 69, 120, 105, 102, 0, 0, 77, 77, 0, 42, 0, 0, 0, 8, 0, 11, 1, 15, 0, 2, 0, 0, 0, 7, 0, 0, 0, 146, 1, 16, 0, 2, 0, 0, 0, 17, 0, 0, 0, 154, 1, 18, 0, 3, 0, 0, 0, 1, 0, 6, 0, 0, 1, 26, 0, 5, 0, 0, 0, 1, 0, 0, 0, 172, 1, 27, 0, 5, 0, 0, 0, 1, 0, 0, 0, 180, 1, 40, 0, 3, 0, 0, 0, 1, 0, 2, 0, 0, 1, 49, 0, 2, 0, 0, 0, 61, 0, 0, 0, 188, 1, 50, 0, 2, 0, 0, 0, 20, 0, 0, 0, 250, 2, 19, 0, 3, 0, 0, 0, 1, 0, 1, 0, 0, 135, 105, 0, 4, 0, 0, 0, 1, 0, 0, 1, 14, 136, 37, 0, 4, 0, 0, 0, 1, 0, 0, 2, 242, 0, 0, 2, 248, 88, 105, 97, 111, 109, 105, 0, 0, 82, 101, 100, 109, 105, 32, 78, 111, 116, 101, 32, 53, 32, 80, 114, 111, 0, 0, 0, 0, 0, 72, 0, 0, 0, 1, 0, 0, 0, 72, 0, 0, 0, 1, 119, 104, 121, 114, 101, 100, 45, 117, 115, 101, 114, 32, 57, 32, 80, 75, 81, 49, 46, 49, 56, 48, 57, 48, 52, 46, 48, 48, 49, 32, 86, 49, 49, 46, 48, 46, 53, 46, 48, 46, 80, 69, 73, 77, 73, 88, 77, 32, 114, 101, 108, 101, 97, 115, 101, 45, 107, 101, 121, 115, 0, 0, 50, 48, 50, 49, 58, 48, 56, 58, 49, 56, 32, 49, 57, 58, 49, 5
You have to decode before using the Base64 string
String encoded = stringToBase64.encode(credentials);
String decoded = stringToBase64.decode(encoded);

Wrong Flutter error Convert image ImagePicker type File to base64, Wrong Encoded

I need to convert an image that I get from the device with ImagePicker, convert it from File type to base64 to store it through a post in a db, but the procedure I do to convert it does not do it well, it converts only a thin line and the rest blank, apparently readAsbyte does not convert well when implemented as "var bytes = imageFile.readAsBytesSync ();" makes it incomplete
this is how i am implementing it
File imageFile;
void _openGallery(BuildContext context) async{
var picture = await ImagePicker().getImage(source: ImageSource.gallery);
this.setState(() {
imageFile = File(picture.path);
var bytes = imageFile.readAsBytesSync();
String imagenConvertida = base64.encode(bytes);
print(bytes);
print(imagenConvertida);
});
Navigator.of(context).pop();
}
From your implementation,
File imageFile;
void _openGallery(BuildContext context) async{
var picture = await ImagePicker().getImage(source: ImageSource.gallery);
this.setState(() {
imageFile = File(picture.path);
var bytes = imageFile.readAsBytesSync();
String imagenConvertida = base64.encode(bytes);
print(bytes);
print(imagenConvertida);
});
Navigator.of(context).pop();
The output of bytes is
[255, 216, 255, 225, 1, 181, 69, 120, 105, 102, 0, 0, 77, 77, 0, 42,
0, 0, 0, 8, 0, 7, 1, 16, 0, 2, 0, 0, 0, 26, 0, 0, 0, 98, 1, 0, 0, 4,
0, 0, 0, 1, 0, 0, 3, 192, 1, 1, 0, 4, 0, 0, 0, 1, 0, 0, 5, 0, 1, 50,
0, 2, 0, 0, 0, 20, 0, 0, 0, 124, 1, 18, 0, 3, 0, 0, 0, 1, 0, 1, 0, 0,
135, 105, 0, 4, 0, 0, 0, 1, 0, 0, 0, 151, 1, 15, 0, 2, 0, 0, 0, 7, 0,
0, 0, 144, 0, 0, 0, 0, 65, 110, 100, 114, 111, 105, 100, 32, 83, 68,
75, 32, 98, 117, 105, 108, 116, 32, 102, 111, 114, 32, 120, 56, 54, 0,
50, 48, 50, 48, 58, 48, 57, 58, 48, 50, 32, 48, 52, 58, 49, 53, 58,
51, 54, 0, 71, 111, 111, 103, 108, 101, 0, 0, 16, 130, 157, 0, 5, 0,
0, 0, 1, 0, 0, 1, 93, 130, 154, 0, 5, 0, 0, 0, 1, 0, 0, 1, 101, 146,
146, 0, 2, 0, 0, 0, 4, 56, 56, 57, 0, 146, 145, 0, 2, 0, 0, 0, 4, 56,
56, 57, 0, 146, 144, 0, 2, 0, 0, 0, 4, 56, 56, 57, 0, 146, 10, 0, 5,
0, 0, 0, 1, 0, 0, 1, 109, 146, 9, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 136,
39, 0, 3, 0, 0, 0, 1, 0, 100, 0, 0, 144, 4, 0, 2, 0, 0, 0, 20, 0, 0,
1, 117, 144, 3, 0, 2, 0, 0, 0, 20, 0,
The output of imagenConvertida is
/9j/4QG1RXhpZgAATU0AKgAAAAgABwEQAAIAAAAaAAAAYgEAAAQAAAABAAADwAEBAAQAAAABAAAFAAEyAAIAAAAUAAAAfAESAAMAAAABAAEAAIdpAAQAAAABAAAAlwEPAAIAAAAHAAAAkAAAAABBbmRyb2lkIFNESyBidWlsdCBmb3IgeDg2ADIwMjA6MDk6MDIgMDQ6MTU6MzYAR29vZ2xlAAAQgp0ABQAAAAEAAAFdgpoABQAAAAEAAAFlkpIAAgAAAAQ4ODkAkpEAAgAAAAQ4ODkAkpAAAgAAAAQ4ODkAkgoABQAAAAEAAAFtkgkAAwAAAAEAAAAAiCcAAwAAAAEAZAAAkAQAAgAAABQAAAF1kAMAAgAAABQAAAGJoAMABAAAAAEAAAUApAMAAwAAAAEAAAAAoAIABAAAAAEAAAPAkgIABQAAAAEAAAGdkgEACgAAAAEAAAGlkAAABwAAAAQwMjIwAAAAAAAAARgAAABkAJiWgDuaygAAABOIAAAD6DIwMjA6MDk6MDIgMDQ6MTU6MzYAMjAyMDowOTowMiAwNDoxNTozNgAAAAEpAAAAZAAAGfMAAAPo/+AAEEpGSUYAAQEAAAEAAQAA/9sAQwACAQEBAQECAQEBAgICAgIEAwICAgIFBAQDBAYFBgYGBQYGBgcJCAYHCQcGBggLCAkKCgoKCgYICwwLCgwJCgoK/9sAQwECAgICAgIFAwMFCgcGBwoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoK/8AAEQgFAAPAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVm
Reversing it, I come up with the following code:
File imageFile;
Image decodedImage;
void _openGallery(BuildContext context) async {
var picture = await ImagePicker().getImage(source: ImageSource.gallery);
this.setState(
() {
imageFile = File(picture.path);
// Convert image to base64
var bytes = imageFile.readAsBytesSync();
String imagenConvertida = base64.encode(bytes);
print('Value of bytes: $bytes');
print('Value of imagenConvertida: $imagenConvertida');
// Convert base64 to image
Uint8List decodedBytes = base64.decode(imagenConvertida);
decodedImage = Image.memory(decodedBytes);
print('Value of decodedBytes: $decodedBytes');
print('Value of decodedImage: $decodedImage');
},
);
// Commented out for testing purposes
// Navigator.of(context).pop();
}
When you compare the output the value of decodedBytes
[255, 216, 255, 225, 1, 181, 69, 120, 105, 102, 0, 0, 77, 77, 0, 42,
0, 0, 0, 8, 0, 7, 1, 16, 0, 2, 0, 0, 0, 26, 0, 0, 0, 98, 1, 0, 0, 4,
0, 0, 0, 1, 0, 0, 3, 192, 1, 1, 0, 4, 0, 0, 0, 1, 0, 0, 5, 0, 1, 50,
0, 2, 0, 0, 0, 20, 0, 0, 0, 124, 1, 18, 0, 3, 0, 0, 0, 1, 0, 1, 0, 0,
135, 105, 0, 4, 0, 0, 0, 1, 0, 0, 0, 151, 1, 15, 0, 2, 0, 0, 0, 7, 0,
0, 0, 144, 0, 0, 0, 0, 65, 110, 100, 114, 111, 105, 100, 32, 83, 68,
75, 32, 98, 117, 105, 108, 116, 32, 102, 111, 114, 32, 120, 56, 54, 0,
50, 48, 50, 48, 58, 48, 57, 58, 48, 50, 32, 48, 52, 58, 49, 53, 58,
51, 54, 0, 71, 111, 111, 103, 108, 101, 0, 0, 16, 130, 157, 0, 5, 0,
0, 0, 1, 0, 0, 1, 93, 130, 154, 0, 5, 0, 0, 0, 1, 0, 0, 1, 101, 146,
146, 0, 2, 0, 0, 0, 4, 56, 56, 57, 0, 146, 145, 0, 2, 0, 0, 0, 4, 56,
56, 57, 0, 146, 144, 0, 2, 0, 0, 0, 4, 56, 56, 57, 0, 146, 10, 0, 5,
0, 0, 0, 1, 0, 0, 1, 109, 146, 9, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 136,
39, 0, 3, 0, 0, 0, 1, 0, 100, 0, 0, 144, 4, 0, 2, 0, 0, 0, 20, 0, 0,
1, 117, 144, 3, 0, 2, 0, 0, 0,
And when you convert it, it will give you the exact original image, see the sample app code below:
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:image_picker/image_picker.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: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// From SO
File imageFile;
Image decodedImage;
void _openGallery(BuildContext context) async {
var picture = await ImagePicker().getImage(source: ImageSource.gallery);
this.setState(
() {
imageFile = File(picture.path);
// Convert image to base64
var bytes = imageFile.readAsBytesSync();
String imagenConvertida = base64.encode(bytes);
print('Value of bytes: $bytes');
print('Value of imagenConvertida: $imagenConvertida');
// Convert base64 to image
Uint8List decodedBytes = base64.decode(imagenConvertida);
decodedImage = Image.memory(decodedBytes);
print('Value of decodedBytes: $decodedBytes');
print('Value of decodedImage: $decodedImage');
},
);
// Commented out for testing purposes
// Navigator.of(context).pop();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SizedBox(
height: 20.0,
),
Text('Original image from gallery'),
SizedBox(height: 10.0),
Container(
color: Colors.blueGrey,
height: 200.0,
width: 150.0,
child: imageFile == null
? Text('Image is not loaded')
: Image.file(imageFile),
),
SizedBox(height: 20.0),
Text('Image decoded from base64'),
SizedBox(height: 10.0),
Container(
color: Colors.grey,
height: 200.0,
width: 150.0,
child: decodedImage == null
? Text('Image is not loaded')
: decodedImage,
),
],
),
),
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.end,
// crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
onPressed: () {
// _getImage();
_openGallery(context);
print('Open Gallery');
},
tooltip: 'Pick an image',
child: Icon(Icons.image),
),
SizedBox(
width: 20,
),
],
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Since the decoded image is the same as the original image, I think var bytes = imageFile.readAsBytesSync(); is working fine.

How To Create Custom Line Chart in Flutter?

I just want to line chart in my flutter application like below image.
Here is my code but the result output is normal. i am using charts_flutter: ^0.9.0 packages.
import 'package:flutter/material.dart';
import 'package:charts_flutter/flutter.dart' as charts;
import 'package:flutter_screenutil/screenutil.dart';
import 'data/sale_data.dart';
class LineChart extends StatelessWidget {
// Defining the data
final data = [
new SalesData(1, 7),
new SalesData(2, 19),
new SalesData(3, 6),
new SalesData(4, 3),
new SalesData(5, 10),
new SalesData(6, 21),
new SalesData(7, 3),
new SalesData(8, 10),
new SalesData(9, 5),
new SalesData(10, 15),
new SalesData(11, 8),
new SalesData(12, 12),
new SalesData(13, 6),
new SalesData(14, 18),
new SalesData(15, 4),
new SalesData(16, 8),
new SalesData(17, 3),
new SalesData(18, 12),
new SalesData(19, 7),
new SalesData(20, 3),
new SalesData(21, 8),
new SalesData(22, 12),
new SalesData(23, 6),
new SalesData(24, 18),
new SalesData(25, 4),
new SalesData(26, 8),
new SalesData(27, 3),
new SalesData(28, 12),
new SalesData(29, 7),
new SalesData(30, 3),
new SalesData(31, 8),
new SalesData(32, 12),
new SalesData(33, 6),
new SalesData(34, 18),
new SalesData(35, 4),
new SalesData(36, 8),
new SalesData(37, 3),
new SalesData(38, 12),
new SalesData(39, 7),
new SalesData(40, 3),
];
_getSeriesData() {
List<charts.Series<SalesData, int>> series = [
charts.Series(
//dot color is fillcolorfn
fillColorFn: (SalesData series, _) =>
charts.MaterialPalette.green.shadeDefault,
//seriesColor: charts.ColorUtil.fromDartColor(Colors.red),
id: "Sales",
data: data,
patternColorFn: (SalesData series, _) => charts.MaterialPalette.white,
// areaColorFn: ((SalesData series, _) => charts.MaterialPalette.black),
domainUpperBoundFn: (SalesData series, _) => series.domainUpper,
domainLowerBoundFn: (SalesData series, _) => series.domainLower,
measureUpperBoundFn: (SalesData series, _) => series.measureUpper,
measureLowerBoundFn: (SalesData series, _) => series.measureLower,
domainFn: (SalesData series, _) => series.year,
measureFn: (SalesData series, _) => series.sales,
colorFn: (SalesData series, _) =>
charts.MaterialPalette.red.shadeDefault),
];
return series;
}
#override
Widget build(BuildContext context) {
return Container(
height: ScreenUtil().setHeight(800),
child: Card(
color: Colors.white,
child: Padding(
padding: EdgeInsets.only(
left: ScreenUtil().setWidth(20),
right: ScreenUtil().setWidth(20),
),
child: Column(
children: <Widget>[
// Text(
// "Sales of a company over the years",
// style: TextStyle(fontWeight: FontWeight.bold),
// ),
// SizedBox(
// height: 20,
// ),
Padding(
padding: EdgeInsets.only(
top: ScreenUtil().setWidth(20),
),
child: Align(
alignment: Alignment.centerRight,
child: Container(
alignment: Alignment.topRight,
color: Colors.white,
height: ScreenUtil().setHeight(50),
width: ScreenUtil().setWidth(100),
child: Text("hello"),
),
),
),
Expanded(
child: new charts.LineChart(
_getSeriesData(),
animate: true,
// domainAxis: new charts.NumericAxisSpec(
// // Set the initial viewport by providing a new AxisSpec with the
// // desired viewport, in NumericExtents.
// viewport: new charts.NumericExtents(0.0, 40.0)),
),
)
],
),
),
),
);
}
}
my output image is below
I've checked the two images that you've shared and the only difference that I can see seems to only be different colors used on those charts. You can try using fl_chart plugin to see if it fits your use case. With fl_chart, it allows you to modify the charts color as detailed on the docs.

Fixed column and row header for DataTable on Flutter Dart

I've build a table on Flutter Dart using DataTable. This table is very large, and I'm using both Vertical and Horizontal scrolling.
When scrolling I lose reference to columns, I need to know what is the column.
As example. On the screenshot i don't know what the numbers 20.0 and 25.0 on the means, unless I scroll to the top.
I've added a GIF example of what i want to achieve. (Using LibreOffice). I need fixed column name (first row).
Example of the table, while scrolling around the middle of the table:
Example of what i want to do:
Code sample for my table:
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
columns: MyDataSet.getColumns(),
rows: widget._data.map<DataRow>((row) => DataRow(
onSelectChanged: (d) {
setState(() {
selectedRow = d ? row.hashCode : null;
});
},
selected: row.hashCode == selectedRow,
cells: MyDataSet.toDataCells(row)
)).toList()
)
),
);
Missing code sample:
return columns.map<DataColumn>((name) => DataColumn(
label: Text(name, style: TextStyle(fontWeight: FontWeight.bold, color: Colors.black),)
)).toList();
Update (24/10/2019)
Current code works well if header name is the same size as cell content. Otherwise both sizes will be different.
Update (21/02/2020)
People created a package to do that. :D
https://pub.dev/packages/table_sticky_headers
Image from pub.dev!
I could come up with a workaround using scroll controllers, looks like this: Video
Basically it's an horizontal scroll for the first row, a vertical scroll for the first column and a mixed horizontal and vertical scroll for the subtable. Then when you move the subtable, its controllers move the column and the row.
Here is a custom widget with an example of how to use it:
final _rowsCells = [
[7, 8, 10, 8, 7],
[10, 10, 9, 6, 6],
[5, 4, 5, 7, 5],
[9, 4, 1, 7, 8],
[7, 8, 10, 8, 7],
[10, 10, 9, 6, 6],
[5, 4, 5, 7, 5],
[9, 4, 1, 7, 8],
[7, 8, 10, 8, 7],
[10, 10, 9, 6, 6],
[5, 4, 5, 7, 5],
[9, 4, 1, 7, 8],
[7, 8, 10, 8, 7],
[10, 10, 9, 6, 6],
[5, 4, 5, 7, 5],
[9, 4, 1, 7, 8]
];
final _fixedColCells = [
"Pablo",
"Gustavo",
"John",
"Jack",
"Pablo",
"Gustavo",
"John",
"Jack",
"Pablo",
"Gustavo",
"John",
"Jack",
"Pablo",
"Gustavo",
"John",
"Jack",
];
final _fixedRowCells = [
"Math",
"Informatics",
"Geography",
"Physics",
"Biology"
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: CustomDataTable(
rowsCells: _rowsCells,
fixedColCells: _fixedColCells,
fixedRowCells: _fixedRowCells,
cellBuilder: (data) {
return Text('$data', style: TextStyle(color: Colors.red));
},
),
);
}
class CustomDataTable<T> extends StatefulWidget {
final T fixedCornerCell;
final List<T> fixedColCells;
final List<T> fixedRowCells;
final List<List<T>> rowsCells;
final Widget Function(T data) cellBuilder;
final double fixedColWidth;
final double cellWidth;
final double cellHeight;
final double cellMargin;
final double cellSpacing;
CustomDataTable({
this.fixedCornerCell,
this.fixedColCells,
this.fixedRowCells,
#required this.rowsCells,
this.cellBuilder,
this.fixedColWidth = 60.0,
this.cellHeight = 56.0,
this.cellWidth = 120.0,
this.cellMargin = 10.0,
this.cellSpacing = 10.0,
});
#override
State<StatefulWidget> createState() => CustomDataTableState();
}
class CustomDataTableState<T> extends State<CustomDataTable<T>> {
final _columnController = ScrollController();
final _rowController = ScrollController();
final _subTableYController = ScrollController();
final _subTableXController = ScrollController();
Widget _buildChild(double width, T data) => SizedBox(
width: width, child: widget.cellBuilder?.call(data) ?? Text('$data'));
Widget _buildFixedCol() => widget.fixedColCells == null
? SizedBox.shrink()
: Material(
color: Colors.lightBlueAccent,
child: DataTable(
horizontalMargin: widget.cellMargin,
columnSpacing: widget.cellSpacing,
headingRowHeight: widget.cellHeight,
dataRowHeight: widget.cellHeight,
columns: [
DataColumn(
label: _buildChild(
widget.fixedColWidth, widget.fixedColCells.first))
],
rows: widget.fixedColCells
.sublist(widget.fixedRowCells == null ? 1 : 0)
.map((c) => DataRow(
cells: [DataCell(_buildChild(widget.fixedColWidth, c))]))
.toList()),
);
Widget _buildFixedRow() => widget.fixedRowCells == null
? SizedBox.shrink()
: Material(
color: Colors.greenAccent,
child: DataTable(
horizontalMargin: widget.cellMargin,
columnSpacing: widget.cellSpacing,
headingRowHeight: widget.cellHeight,
dataRowHeight: widget.cellHeight,
columns: widget.fixedRowCells
.map((c) =>
DataColumn(label: _buildChild(widget.cellWidth, c)))
.toList(),
rows: []),
);
Widget _buildSubTable() => Material(
color: Colors.lightGreenAccent,
child: DataTable(
horizontalMargin: widget.cellMargin,
columnSpacing: widget.cellSpacing,
headingRowHeight: widget.cellHeight,
dataRowHeight: widget.cellHeight,
columns: widget.rowsCells.first
.map((c) => DataColumn(label: _buildChild(widget.cellWidth, c)))
.toList(),
rows: widget.rowsCells
.sublist(widget.fixedRowCells == null ? 1 : 0)
.map((row) => DataRow(
cells: row
.map((c) => DataCell(_buildChild(widget.cellWidth, c)))
.toList()))
.toList()));
Widget _buildCornerCell() =>
widget.fixedColCells == null || widget.fixedRowCells == null
? SizedBox.shrink()
: Material(
color: Colors.amberAccent,
child: DataTable(
horizontalMargin: widget.cellMargin,
columnSpacing: widget.cellSpacing,
headingRowHeight: widget.cellHeight,
dataRowHeight: widget.cellHeight,
columns: [
DataColumn(
label: _buildChild(
widget.fixedColWidth, widget.fixedCornerCell))
],
rows: []),
);
#override
void initState() {
super.initState();
_subTableXController.addListener(() {
_rowController.jumpTo(_subTableXController.position.pixels);
});
_subTableYController.addListener(() {
_columnController.jumpTo(_subTableYController.position.pixels);
});
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Row(
children: <Widget>[
SingleChildScrollView(
controller: _columnController,
scrollDirection: Axis.vertical,
physics: NeverScrollableScrollPhysics(),
child: _buildFixedCol(),
),
Flexible(
child: SingleChildScrollView(
controller: _subTableXController,
scrollDirection: Axis.horizontal,
child: SingleChildScrollView(
controller: _subTableYController,
scrollDirection: Axis.vertical,
child: _buildSubTable(),
),
),
),
],
),
Row(
children: <Widget>[
_buildCornerCell(),
Flexible(
child: SingleChildScrollView(
controller: _rowController,
scrollDirection: Axis.horizontal,
physics: NeverScrollableScrollPhysics(),
child: _buildFixedRow(),
),
),
],
),
],
);
}
}
Since the first column, the first row and the subtable are independent, I had to create a DataTable for each one. And since DataTable has headers that can't be removed, the headers of the first column and the subtable are hidden by the first row.
Also, I had to make the first column and first row not manually scrollable because if you scroll them the subtable won't scroll.
This might not be the best solution, but at the moment doesn't seem to be another way to do it. You could try to improve this approach, maybe using Table or other widgets instead of DataTable at least you could avoid hiding the headers of the subtable and first column.
A few months back I had similar issue with limmited capabilities of stock DataTable and PaginatedDataTable2 widgets which didn't allow to fix the header. Eventually I took those widgets appart and created my own versions but with blackjack and fixed header row. Here's the plug-in on pub.dev:
https://pub.dev/packages/data_table_2
The classes DataTable2 and PaginatedDataTable2 provide exactly the same APIs as the original versions.
NOTE: these one only implement sticky top rows, leftmost columns are not fixed/sticky
Try the flutter package horizontal_data_table
A Flutter Widget that create a horizontal table with fixed column on left hand side.
dependencies:
horizontal_data_table: ^2.5.0
Below Given Pablo Barrera answer is quite interesting, I have corrected and modified this answer with an enhanced feature, also it is easy to handle DataTable with fixed rows and columns. This DataTable you can customize as per your requirements.
import 'package:flutter/material.dart';
class DataTablePage extends StatelessWidget {
const DataTablePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Expanded(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: CustomDataTable(
fixedCornerCell: '',
borderColor: Colors.grey.shade300,
rowsCells: _rowsCells,
fixedColCells: _fixedColCells,
fixedRowCells: _fixedRowCells,
),
),
),
),
);
}
}
final _rowsCells = [
[7, 8, 10, 8, 7],
[10, 10, 9, 6, 6],
[5, 4, 5, 7, 5],
[9, 4, 1, 7, 8],
[7, 8, 10, 8, 7],
[10, 10, 9, 6, 6],
[5, 4, 5, 7, 5],
[9, 4, 1, 7, 8],
[7, 8, 10, 8, 7],
[10, 10, 9, 6, 6],
[5, 4, 5, 7, 5],
[9, 4, 1, 7, 8],
[7, 8, 10, 8, 7],
[10, 10, 9, 6, 6],
[5, 4, 5, 7, 5],
[9, 4, 1, 7, 8]
];
final _fixedColCells = [
"Pablo",
"Gustavo",
"John",
"Jack",
"Pablo",
"Gustavo",
"John",
"Jack",
"Pablo",
"Gustavo",
"John",
"Jack",
"Pablo",
"Gustavo",
"John",
"Jack",
];
final _fixedRowCells = [
"Math",
"Informatics",
"Geography",
"Physics",
"Biology"
];
class CustomDataTable<T> extends StatefulWidget {
final T fixedCornerCell;
final List<T> fixedColCells;
final List<T> fixedRowCells;
final List<List<T>> rowsCells;
final double fixedColWidth;
final double cellWidth;
final double cellHeight;
final double cellMargin;
final double cellSpacing;
final Color borderColor;
const CustomDataTable({
super.key,
required this.fixedCornerCell,
required this.fixedColCells,
required this.fixedRowCells,
required this.rowsCells,
this.fixedColWidth = 60.0,
this.cellHeight = 56.0,
this.cellWidth = 120.0,
this.cellMargin = 10.0,
this.cellSpacing = 10.0,
required this.borderColor,
});
#override
State<StatefulWidget> createState() => CustomDataTableState();
}
class CustomDataTableState<T> extends State<CustomDataTable<T>> {
final _columnController = ScrollController();
final _rowController = ScrollController();
final _subTableYController = ScrollController();
final _subTableXController = ScrollController();
Widget _buildChild(double width, T data) => SizedBox(
width: width,
child: Text(
'$data',
textAlign: TextAlign.center,
),
);
TableBorder _buildBorder({
bool top = false,
bool left = false,
bool right = false,
bool bottom = false,
bool verticalInside = false,
}) {
return TableBorder(
top: top ? BorderSide(color: widget.borderColor) : BorderSide.none,
left: left ? BorderSide(color: widget.borderColor) : BorderSide.none,
right: right ? BorderSide(color: widget.borderColor) : BorderSide.none,
bottom: bottom ? BorderSide(color: widget.borderColor) : BorderSide.none,
verticalInside: verticalInside
? BorderSide(color: widget.borderColor)
: BorderSide.none,
);
}
Widget _buildFixedCol() => DataTable(
border: _buildBorder(right: true),
horizontalMargin: widget.cellMargin,
columnSpacing: widget.cellSpacing,
headingRowHeight: widget.cellHeight,
dataRowHeight: widget.cellHeight,
columns: [
DataColumn(
label:
_buildChild(widget.fixedColWidth, widget.fixedColCells.first))
],
rows: widget.fixedColCells
.map((c) =>
DataRow(cells: [DataCell(_buildChild(widget.fixedColWidth, c))]))
.toList());
Widget _buildFixedRow() => DataTable(
border: _buildBorder(verticalInside: true, bottom: true),
horizontalMargin: widget.cellMargin,
columnSpacing: widget.cellSpacing,
headingRowHeight: widget.cellHeight,
dataRowHeight: widget.cellHeight,
columns: widget.fixedRowCells
.map(
(c) => DataColumn(
label: _buildChild(widget.cellWidth, c),
),
)
.toList(),
rows: const [],
);
Widget _buildSubTable() => Material(
color: Colors.white,
child: DataTable(
border: _buildBorder(verticalInside: true),
horizontalMargin: widget.cellMargin,
columnSpacing: widget.cellSpacing,
headingRowHeight: widget.cellHeight,
dataRowHeight: widget.cellHeight,
columns: widget.rowsCells.first
.map((c) => DataColumn(label: _buildChild(widget.cellWidth, c)))
.toList(),
rows: widget.rowsCells
.map(
(row) => DataRow(
cells: row
.map((c) => DataCell(_buildChild(widget.cellWidth, c)))
.toList()),
)
.toList()));
Widget _buildCornerCell() => DataTable(
border: _buildBorder(bottom: true, right: true),
horizontalMargin: widget.cellMargin,
columnSpacing: widget.cellSpacing,
headingRowHeight: widget.cellHeight,
dataRowHeight: widget.cellHeight,
columns: [
DataColumn(
label: _buildChild(
widget.fixedColWidth,
widget.fixedCornerCell,
),
)
],
rows: const [],
);
#override
void initState() {
super.initState();
_subTableXController.addListener(() {
_rowController.jumpTo(_subTableXController.position.pixels);
});
_subTableYController.addListener(() {
_columnController.jumpTo(_subTableYController.position.pixels);
});
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border.all(color: widget.borderColor),
),
child: Column(
children: [
Row(
children: [
_buildCornerCell(),
Flexible(
child: SingleChildScrollView(
controller: _rowController,
scrollDirection: Axis.horizontal,
physics: const NeverScrollableScrollPhysics(),
child: _buildFixedRow(),
),
),
],
),
Expanded(
child: Row(
children: [
SingleChildScrollView(
controller: _columnController,
scrollDirection: Axis.vertical,
physics: const NeverScrollableScrollPhysics(),
child: _buildFixedCol(),
),
Flexible(
child: SingleChildScrollView(
physics: const ClampingScrollPhysics(),
controller: _subTableXController,
scrollDirection: Axis.horizontal,
child: SingleChildScrollView(
physics: const ClampingScrollPhysics(),
controller: _subTableYController,
scrollDirection: Axis.vertical,
child: _buildSubTable(),
),
),
),
],
),
),
],
),
);
}
}
Output: