import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:multimedia/widgets/movies/add/add_screen.dart'; import 'package:multimedia/widgets/movies/details/details_screen.dart'; import 'package:multimedia/widgets/movies/filter_box.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:intl/intl.dart'; import 'package:http/http.dart' as http; import 'package:syncfusion_flutter_datagrid/datagrid.dart'; import 'package:syncfusion_flutter_core/theme.dart'; import 'package:multimedia/data/movies.dart'; import 'package:multimedia/constants.dart'; import 'package:logger/logger.dart'; import 'package:multimedia/log_printer.dart'; class MoviesScreen extends StatefulWidget { const MoviesScreen({ super.key, }); @override State createState() => _MoviesScreenState(); } class _MoviesScreenState extends State { final DataGridController _dataGridController = DataGridController(); late MovieDataSource movieDataSource; List _columns = []; late List _moviesList; String searchText = ''; bool showInit = false; bool showProg = false; bool showDone = false; Logger logger = getLogger(); Future> generateMovieList() async { SharedPreferences prefs = await SharedPreferences.getInstance(); String? dbProtocol = prefs.getString('dbProtocol'); String? dbHost = prefs.getString('dbHost'); String? dbPath = prefs.getString('dbPath'); Uri url = Uri( scheme: dbProtocol, host: dbHost, path: dbPath, queryParameters: { 'module': 'movie', 'function': 'getList', }, ); try { final response = await http.get(url); //LATER bool? error; //LATER String? errorMessage; Map movies = json.decode(response.body); //LATER error = movies['error']; //LATER errorMessage = movies['errmsg']; var list = movies['data']; List moviesTmp = await list.map((json) => Movie.fromJson(json)).toList(); movieDataSource = MovieDataSource(moviesTmp); _moviesList = moviesTmp; return moviesTmp; } catch (e) { print(e.toString()); } return []; } List getColumns() { return [ GridColumn( columnName: 'movieID', visible: false, width: 70, label: Container( padding: const EdgeInsets.all(16.0), alignment: Alignment.centerLeft, child: const Text('movieID'))), GridColumn( columnName: 'state', visible: false, width: 70, label: Container( padding: const EdgeInsets.all(16.0), alignment: Alignment.centerLeft, child: const Text('state'))), GridColumn( columnName: 'backdropPath', visible: false, width: 70, label: Container( padding: const EdgeInsets.all(16.0), alignment: Alignment.centerLeft, child: const Text('backdropPath'))), GridColumn( columnName: 'movieTitle', columnWidthMode: ColumnWidthMode.auto, label: Container( padding: const EdgeInsets.all(8.0), alignment: Alignment.centerLeft, child: const Text('Title'))), GridColumn( columnName: 'releaseDate', columnWidthMode: ColumnWidthMode.auto, label: Container( padding: const EdgeInsets.all(8.0), alignment: Alignment.centerLeft, child: const Text('First Aired'))), GridColumn( columnName: 'resolution', columnWidthMode: ColumnWidthMode.auto, label: Container( padding: const EdgeInsets.all(8.0), alignment: Alignment.centerLeft, child: const Text('Resolution'))), GridColumn( columnName: 'overview', columnWidthMode: ColumnWidthMode.auto, label: Container( padding: const EdgeInsets.all(8.0), alignment: Alignment.centerLeft, child: const Text('Overview'))), ]; } @override void initState() { super.initState(); _columns = getColumns(); } Future showMovieDetails(int index) async { DataGridRow row = movieDataSource.effectiveRows.elementAt(index); List> cells = row.getCells(); String movieID = cells[MovieColumn.movieID].value.toString(); bool found = false; int movieIndex; int realIndex; movieDataSource.clearFilters(); realIndex = movieDataSource.rows.indexOf(row); for (movieIndex = 0; movieIndex < _moviesList.length; movieIndex++) { if (_moviesList[movieIndex].movieID == int.parse(movieID)) { found = true; break; } } if (found) { final m = await Navigator.push( context, MaterialPageRoute( builder: (ctx) => MovieDetailsScreen(movie: _moviesList[movieIndex]))); if (m != null) { _moviesList[movieIndex].movieTitle = m.movieTitle; _moviesList[movieIndex].localPath = m.localPath; _moviesList[movieIndex].resolution = m.resolution; _moviesList[movieIndex].state = m.state; Movie e = _moviesList[movieIndex]; movieDataSource._movieDataGridRows[realIndex] = DataGridRow(cells: [ DataGridCell(columnName: 'movieID', value: e.movieID), DataGridCell(columnName: 'state', value: e.state), DataGridCell( columnName: 'backdropPath', value: e.backdropPath), DataGridCell(columnName: 'movieTitle', value: e.movieTitle), DataGridCell( columnName: 'releaseDate', value: DateFormat('yyyy-MM-dd').format(e.releaseDate)), DataGridCell(columnName: 'resolution', value: e.resolution), DataGridCell(columnName: 'overview', value: e.overview), ]); for (int i = 0; i < _columns.length; i++) { movieDataSource.updateDataGridSource( rowColumnIndex: RowColumnIndex(realIndex, i)); } } else {} filter(); } } void setFilter(String text, bool sInit, bool sProg, bool sDone) { searchText = text; showInit = sInit; showProg = sProg; showDone = sDone; filter(); } void filter() { movieDataSource.clearFilters(); if (showInit) movieDataSource.addFilter( 'state', FilterCondition( type: FilterType.equals, value: 1, filterOperator: FilterOperator.or), ); if (showProg) movieDataSource.addFilter( 'state', FilterCondition( type: FilterType.equals, value: 2, filterOperator: FilterOperator.or, )); if (showDone) movieDataSource.addFilter( 'state', FilterCondition( type: FilterType.equals, value: 3, filterOperator: FilterOperator.or, )); movieDataSource.addFilter( 'movieTitle', FilterCondition( type: FilterType.beginsWith, value: searchText, filterOperator: FilterOperator.and, filterBehavior: FilterBehavior.stringDataType, )); } late SfDataGrid dataGrid; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Movies'), actions: [ const VerticalDivider( width: 10, thickness: 3, indent: 0, endIndent: 0, color: Colors.grey, ), IconButton( icon: const Icon(Icons.add_circle_outline), tooltip: 'Add Movie', onPressed: () { addMovie(); }, ), IconButton( icon: const Icon(Icons.update_outlined), tooltip: 'Update movie', onPressed: () { updateMovies(); //todo }, ), ], ), body: Column(children: [ MovieFilterBox(setFilter: setFilter), Expanded( child: FutureBuilder( future: generateMovieList(), builder: (context, data) { if (data.hasData) { dataGrid = SfDataGrid( source: movieDataSource, selectionMode: SelectionMode.single, navigationMode: GridNavigationMode.row, columns: _columns, controller: _dataGridController, columnWidthMode: ColumnWidthMode.auto, columnWidthCalculationRange: ColumnWidthCalculationRange.allRows, onCellTap: (DataGridCellTapDetails details) { int index = details.rowColumnIndex.rowIndex - 1; if (index < 0) { return; } showMovieDetails(index); }, onCellDoubleTap: (DataGridCellDoubleTapDetails details) { print(details.rowColumnIndex); }, onCellLongPress: (DataGridCellLongPressDetails details) { print(details.rowColumnIndex); }, onCellSecondaryTap: (DataGridCellTapDetails details) { print(details.rowColumnIndex); }, ); return SfDataGridTheme( data: const SfDataGridThemeData( headerColor: Color(0xff009889), ), child: dataGrid, ); } else { return const Center( child: CircularProgressIndicator( strokeWidth: 2, value: 0.8, ), ); } }, ), ), ]), ); } void addMovieToList(Movie movie) { String movieTitle = movie.movieTitle.toUpperCase().replaceAll('Ä', 'a').replaceAll('Ö', 'ö').replaceAll('Ü', 'ü').replaceAll('ß', 'ss'); _moviesList.add(movie); DataGridRow newRow = DataGridRow(cells: [ DataGridCell(columnName: 'movieID', value: movie.movieID), DataGridCell(columnName: 'state', value: movie.state), DataGridCell( columnName: 'backdropPath', value: movie.backdropPath), DataGridCell(columnName: 'movieTitle', value: movie.movieTitle), DataGridCell( columnName: 'releaseDate', value: DateFormat('yyyy-MM-dd').format(movie.releaseDate)), DataGridCell(columnName: 'resolution', value: movie.resolution), DataGridCell(columnName: 'overview', value: movie.overview), ]); int rowNum = -1; for (int i = 0; i < movieDataSource._movieDataGridRows.length; i++) { DataGridRow tmpRow = movieDataSource._movieDataGridRows[i]; List tmpCells = tmpRow.getCells(); String tmpTitle = tmpCells[3].value.toString().toUpperCase().replaceAll('Ä', 'a').replaceAll('Ö', 'ö').replaceAll('Ü', 'ü').replaceAll('ß', 'ss'); if (tmpTitle.toUpperCase().compareTo(movieTitle) >= 0) { rowNum = i; break; } } if (rowNum == -1) { rowNum = movieDataSource._movieDataGridRows.length; } movieDataSource.rows.insert(rowNum, newRow); movieDataSource.notifyListeners(); } void addMovieFinished() {} void addMovie() { /*final m =*/ Navigator.push( context, MaterialPageRoute( builder: (ctx) => AddMovieScreen(addMovieToList, addMovieFinished))); } void searchMovie() { logger.i('search movie'); } void updateMovies() { logger.i('update movies'); } } class MovieDataSource extends DataGridSource { /// Creates the employee data source class with required details. MovieDataSource(this.movies) { buildDataGridRow(); } void addDataGridRow(DataGridRow newRow) { rows.add(newRow); // To refresh the DataGrid based on CRUD operation. notifyListeners(); } Logger logger = getLogger(); void buildDataGridRow() { _movieDataGridRows = movies .map((e) => DataGridRow(cells: [ DataGridCell(columnName: 'movieID', value: e.movieID), DataGridCell(columnName: 'state', value: e.state), DataGridCell( columnName: 'backdropPath', value: e.backdropPath), DataGridCell( columnName: 'movieTitle', value: e.movieTitle), DataGridCell( columnName: 'releaseDate', value: DateFormat('yyyy-MM-dd').format(e.releaseDate)), DataGridCell( columnName: 'resolution', value: e.resolution), DataGridCell(columnName: 'overview', value: e.overview), ])) .toList(); } List movies = []; List _movieDataGridRows = []; @override List get rows => _movieDataGridRows; @override DataGridRowAdapter buildRow(DataGridRow row) { return DataGridRowAdapter( cells: row.getCells().map((e) { Color colorBG = Colors.white; Color colorFG = Colors.black; if (e.columnName == 'movieTitle') { int state = row.getCells().toList()[MovieColumn.movieState].value; if (state == 1) { colorBG = StateColor.init; } else if (state == 2) { colorBG = StateColor.prog; colorFG = Colors.white; } else { colorBG = StateColor.done; } } return Container( alignment: Alignment.centerLeft, padding: const EdgeInsets.all(8.0), color: colorBG, child: Text( e.value.toString(), style: TextStyle(color: colorFG), ), ); }).toList()); } void updateDataGrid() { notifyListeners(); } void updateDataGridSource({required RowColumnIndex rowColumnIndex}) { notifyDataSourceListeners(rowColumnIndex: rowColumnIndex); } }