bloc.dart
part 'news_article_loader_event.dart';
part 'news_article_loader_state.dart';
part 'news_article_loader_bloc.freezed.dart';
#injectable
class NewsArticleLoaderBloc
extends Bloc<NewsArticleLoaderEvent, NewsArticleLoaderState> {
NewsArticleLoaderBloc(this.iBlogPost)
: super(NewsArticleLoaderState.initial());
final INewsArticle iBlogPost;
#override
Stream<NewsArticleLoaderState> mapEventToState(
NewsArticleLoaderEvent event) async* {
yield NewsArticleLoaderState.loadInProgress();
final failureOrSuccess = await iBlogPost.getDataNews();
yield failureOrSuccess.fold((l) => NewsArticleLoaderState.loadFailure(l),
(r) => NewsArticleLoaderState.loadSuccess(r));
}
}
repository.dart
import 'dart:convert';
import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart';
import 'package:injectable/injectable.dart';
import 'package:mone/app_constant.dart';
import 'package:mone/model/news/blog_post.dart';
import 'package:mone/repositories/i_news_repository.dart';
import 'package:shared_preferences/shared_preferences.dart';
#Injectable(as: INewsArticle)
class BLogPostRepository implements INewsArticle {
final Dio dio;
// int page = 1;
final SharedPreferences prefs;
BLogPostRepository(this.dio, this.prefs);
#override
Future<Either<String, List<NewsBlogModel>>> getDataNews() async {
try {
final token = prefs.get(kAccesTokenKey);
String url = '/api/v1/cms?size=30';
var response = await dio.get(url,
options: Options(
headers: {
'Accept': 'aplication/json',
'Authorization': 'bearer $token',
},
));
if (response.statusCode == 200) {
final listNews = (response.data['items'] as List)
.map((e) => NewsBlogModel.fromJson(e as Map<String, dynamic>))
.toList();
return right(listNews);
}
return left('error: ${response.statusCode}');
} catch (e) {
return left('error get data $e');
}
}
#override
Future<Either<String, List<NewsBlogModel>>> getDataNewsbyId() async {
// TODO: implement getDataNewsbyId
try {
final token = prefs.get(kAccesTokenKey);
String urlbyid = '/api/v1/cms/id';
var response = await dio.get(urlbyid,
options: Options(headers: {
'Accept': 'aplication/json',
'Authorization': 'bearer $token',
}));
if (response.statusCode == 200) {
final dataNewsbyId = (response.data['items'] as List)
.map((e) => NewsBlogModel.fromJson(e as Map<String, dynamic>))
.toList();
return right(dataNewsbyId);
}
return left("error:${response.statusCode}");
} catch (e) {
return left("error get data $e");
}
}
}
I have the code below where I have provided the BLoC code for the code and also for the repository code. but I'm still confused about how to connect it to the UI that I made. explanation please.
below also my code has succeeded to call all data list from API. but when I want to try to find the data list in the get, but can't.
UI.dart
part of '../screens.dart';
class NewsScreen extends StatefulWidget {
#override
_NewsScreenState createState() => _NewsScreenState();
}
class _NewsScreenState extends State<NewsScreen> {
Widget customTitleBar = const Text('Berita');
Icon customIcon = new Icon(Icons.search, color: primaryColor);
TextEditingController filterController = TextEditingController();
// String filter = '';
// void initState(){
// }
#override
Widget build(BuildContext context) {
// bloc provider berguna untuk menambahkan data dari bloc ke ui,
return BlocProvider(
create: (context) =>
getIt<NewsArticleLoaderBloc>()..add(NewsArticleLoaderEvent.started()),
child: Scaffold(
appBar: AppBar(
title: customTitleBar,
actions: <Widget>[
IconButton(
onPressed: () {
setState(() {
if (this.customIcon.icon == Icons.search) {
this.customIcon =
new Icon(Icons.close, color: primaryColor, size: 30);
this.customTitleBar = new TextField(
style: new TextStyle(
color: Colors.white,
),
decoration: new InputDecoration(
prefixIcon: new Icon(Icons.search,
color: primaryColor, size: 30),
hintText: "Search...",
hintStyle: new TextStyle(color: Colors.white)),
cursorColor: primaryColor,
onChanged: (value) async {
if (value.isEmpty) {
setState(() {});
return;
}
},
controller:filterController,
);
} else {
this.customIcon =
new Icon(Icons.search, color: primaryColor, size: 30);
this.customTitleBar = new Text("Berita");
}
});
},
icon: customIcon,
),
],
),
body: BlocBuilder<NewsArticleLoaderBloc, NewsArticleLoaderState>(
builder: (context, state) {
return state.map(
initial: (_) => Container(),
loadInProgress: (_) => Center(
child: SpinKitCircle(
color: primaryColor,
),
),
loadFailure: (state) => Center(
child: Text(state.failure),
),
loadSuccess: (state) {
if (state.datenews.isEmpty) {
return Center(
child: Text("data tidak di temukan"),
);
} else {
return ListView.separated(
padding: EdgeInsets.all(8.0),
itemCount: state.datenews.length,
separatorBuilder: (BuildContext context, int index) {
return SizedBox(
height: 4,
);
},
itemBuilder: (BuildContext context, int index) {
final newsBlog = state.datenews[index];
return ContentNews(
newsBlog: newsBlog,
);
},
);
}
},
);
},
),
),
);
}
}
Related
I am fetching the user data through the fetchUser() method on the home screen but when I update the income, it is not updated on the home screen when I navigate back. How can I make the method fetchUser be called each time I navigate to the home screen?
Or what else I can do so I can achieve this?
import 'dart:async';
import 'dart:convert';
import 'package:fin_app/apiservice/variables.dart';
import 'package:month_year_picker/month_year_picker.dart';
// import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:fin_app/screens/login_screen/components/overview_screen/top_card.dart';
import 'package:flutter/material.dart';
import '../../models/user.dart';
import '../monthly_expense/expense_app_theme.dart';
import 'input_form.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String dropdownValue = list.first;
List userData = [];
#override
void initState() {
super.initState();
fetchUser();
}
Future<User> fetchUser() async {
Map<String, String> requestHeaders = {
'Content-type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token'
};
final response = await http.get(
Uri.parse('https://10.0.2.2:7014/api/user/me'),
headers: requestHeaders,
);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
// income=jsonDecode(response.body)['income'];
income=jsonDecode(response.body)['profile']['income'];
return User.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
print(response.statusCode.toString());
throw Exception('Failed to load user');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: ExpenseAppTheme.background,
body: Column(
children: [
SizedBox(height: 15),
TopNeuCard(balance: '\ 20,000', expense: '100'),
Expanded(
child: Container(
child: Center(
child: Column(
children: [
Container(
padding: EdgeInsets.only(left: 30.00),
alignment: Alignment.topLeft,
child: DropdownButton<String>(
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 100,
style: const TextStyle(
color: Color.fromARGB(255, 44, 121, 244)),
underline: Container(
height: 2,
color: Color.fromARGB(255, 44, 121, 244),
),
onChanged: (String? value) {
// This is called when the user selects an item.
setState(() {
dropdownValue = value!;
});
_fetchData();
},
items: list.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
Container(height: 275, child: _buildListView(context))
],
)),
)),
],
),
floatingActionButton: FloatingActionButton(
onPressed: navigate,
backgroundColor: Color.fromARGB(255, 121, 146, 237),
child: const Icon(Icons.add_rounded),
),
);
}
Future<void> _fetchData() async {
var apiUrl2 =
'https://10.0.2.2:7014/api/expense/me/month?month=2022%20$dropdownValue';
Map<String, String> requestHeaders = {
'Content-type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token'
};
final response = await http.get(
Uri.parse(apiUrl2),
headers: requestHeaders,
);
final data = json.decode(response.body);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
setState(() {
userData = data;
});
} else if (response.statusCode == 404) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text("No expenses present")));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
print(response.statusCode.toString());
throw Exception('Failed to load expenses');
}
}
Future deletePost(String id) async {
var apiUrl = 'https://10.0.2.2:7014/api/expense/me/$id';
// final res = await http.delete(Uri.parse(apiUrl));
// if (res.statusCode == 200) {
// print("Deleted");
// } else {
// throw "Sorry! Unable to delete this post.";
// }
Map<String, String> requestHeaders = {
'Content-type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token'
};
final response = await http.delete(
Uri.parse(apiUrl),
headers: requestHeaders,
);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text("Deleted succesfully")));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
print(response.statusCode.toString());
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Expense could not be deleted ")));
throw Exception('Failed to load expenses');
}
}
void navigate() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const FormInput()),
);
}
ListView _buildListView(BuildContext context) {
return ListView.builder(
itemCount: userData.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text(userData[index]["category"]),
subtitle: Text(userData[index]["date"]),
leading: IconButton(
icon: Icon(Icons.delete),
color: Colors.red,
onPressed: () {
setState(() {
deletePost(userData[index]["id"]);
userData.removeAt(index);
});
},
),
trailing: Text(
'-' '\$' + userData[index]["amount"].toString(),
style: TextStyle(
//fontWeight: FontWeight.bold,
fontSize: 16,
color: Colors.red,
),
),
onTap: () {},
));
},
);
}
}
Change this:
void navigate() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const FormInput()),
);
}
to this:
void navigate() async {
bool result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const FormInput()),
);
if(result != null && result){
fetchUser();
}
}
then inside FormInput, pop like this:
Navigator.pop(context, true);
I'm a new Flutter user, here I'm having a problem, I'm confused about how to make a search that reads from JSON, here I've created a search menu but I don't understand how to connect to the database. Can you help me to modify my source code so I can do a search. Hopefully good friends can help me make a search function. Thank you friend
datanasabah.dart
GlobalKey<ScaffoldState> _scaffoldState = GlobalKey<ScaffoldState>();
class DataNasabah extends StatefulWidget {
DataNasabah({Key key}) : super(key: key);
final String title = "Data Nasabah";
#override
_DataNasabahState createState() => _DataNasabahState();
}
class _DataNasabahState extends State<DataNasabah> {
ApiService apiService;
ListNasabah _listNasabah;
bool _isLoading = true;
List<Nasabah> data;
final _textHeadStyle =
TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold);
void fetchData() async {
final res = await apiService.getNasabah();
data = res;
setState(() {
_isLoading = false;
});
}
#override
void initState() {
super.initState();
apiService = ApiService();
_listNasabah = new ListNasabah(apiService: apiService);
fetchData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldState,
appBar: AppBar(
title: Text(
'Data Nasabah',
style: TextStyle(color: Colors.white),
),
actions: <Widget>[
MaterialButton(
elevation: 5,
child: Text("CARI", style: _textHeadStyle),
onPressed: () async {
var value = await showTopModalSheet<String>(
context: context, child: DumyModal());
},
),
],
),
body: _listNasabah.createViewList(),
);
}
}
class DumyModal extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
height: MediaQuery.of(context).size.height * .2,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextField(
decoration: InputDecoration(
labelText: "NIK Nasabah", //babel text
hintText: "Masukkan NIK Nasabah", //hint text
prefixIcon: Icon(Icons.people), //prefix iocn
hintStyle: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, ), //hint text style
labelStyle: TextStyle(fontSize: 17, color: Colors.black), //label style
)
),
MaterialButton(
color: Colors.orange,
child: const Text("Cari", style: TextStyle(color: Colors.white),),
onPressed: () {
FocusScope.of(context).requestFocus(FocusNode());
},
)
],
),
);
}
}
nasabah_service.dart
class ApiService {
final String baseUrl = '192.168.100.207:8080';
Client client = Client();
Future<List<Nasabah>> getNasabah() async {
final response = await client.get('http://$baseUrl/api/mstdebitur');
print(response.body);
if (response.statusCode == 200) {
final nasabah = (json.decode(response.body) as List)
.map((e) => Nasabah.fromMap(e))
.toList();
return nasabah;
} else {
throw Exception('Failed to load post');
}
}
Future<bool> createNasabah(Nasabah data) async {
String url = new Uri.http("$baseUrl", "/api/mstdebitur/").toString();
final response = await client.post(url,
headers: {"Content-Type": "application/json"},
body: nasabahToJson(data));
if (response.statusCode == 201) {
return true;
} else {
return false;
}
}
Future<bool> updateNasabah(Nasabah data) async {
String url =
new Uri.http("$baseUrl", "/api/mstdebitur/${data.id}").toString();
final response = await client.put(url,
headers: {"Content-Type": "application/json"},
body: nasabahToJson(data));
if (response.statusCode == 200) {
return true;
} else {
return false;
}
}
Future<bool> deleteNasabah(int id) async {
String url = new Uri.http("$baseUrl", "/api/mstdebitur/$id").toString();
final response = await client.delete(url);
if (response.statusCode == 200) {
return true;
} else {
return false;
}
}
}
I was trying to update data (add, edit, delete) from my list using API, when I clicked something to update the list e.g deleting an item it showed a successfully updated. but in the Debug Console, there's an error that says
The following assertion was thrown while finalizing the widget tree:
Duplicate GlobalKey detected in the widget tree. The following GlobalKey
was specified multiple times in the widget tree. This will lead to
parts of the widget tree being truncated unexpectedly, because the
second time a key is seen, the previous instance is moved to the new
location. The key was:
[LabeledGlobalKey #47c37] This was determined by noticing that after the widget with the above global key was moved out
of its previous parent, that previous parent never updated during this
frame, meaning that it either did not update at all or updated before
the widget was moved, in either case implying that it still thinks
that it should have a child with that global key.
And when I tried going back to the Previous screen it just showed Blank Screen
Here's the code
Model
import 'dart:convert';
class Nasabah {
int id;
String nama_debitur;
String alamat;
String no_telp;
String no_ktp;
String no_selular;
Nasabah({
this.id = 0,
this.nama_debitur,
this.alamat,
this.no_telp,
this.no_ktp,
this.no_selular,
});
factory Nasabah.fromJson(Map<String, dynamic> json) => Nasabah(
id: json["id"],
nama_debitur: json["nama_debitur"],
alamat: json["alamat"],
no_telp: json["no_telp"],
no_ktp: json["no_ktp"],
no_selular: json["no_selular"],
);
factory Nasabah.fromMap(Map<String, dynamic> map) => Nasabah(
id: map["id"],
nama_debitur: map["nama_debitur"],
alamat: map["alamat"],
no_telp: map["no_telp"],
no_ktp: map["no_ktp"],
no_selular: map["no_selular"],
);
Map<String, dynamic> toJson() => {
"id": id,
"nama_debitur": nama_debitur,
"alamat": alamat,
"no_telp": no_telp,
"no_ktp": no_ktp,
"no_selular": no_selular,
};
#override
String toString() {
return 'Nasabah{id: $id, nama_debitur: $nama_debitur, alamat: $alamat, no_telp: $no_telp, no_ktp: $no_ktp, no_selular: $no_selular}';
}
}
class NasabahResult {
String status;
List<Nasabah> data = new List<Nasabah>();
NasabahResult({
this.status,
this.data,
});
factory NasabahResult.fromJson(Map<String, dynamic> data) => NasabahResult(
status: data["status"],
data: List<Nasabah>.from(
data["data"].map((item) => Nasabah.fromJson(item))),
);
}
NasabahResult nasabahResultFromJson(String jsonData) {
final data = json.decode(jsonData);
return NasabahResult.fromJson(data);
}
String nasabahToJson(Nasabah nasabah) {
final jsonData = nasabah.toJson();
return json.encode(jsonData);
}
Service
import 'package:flutter_auth/Models/nasabah.dart';
import 'dart:convert';
import 'package:http/http.dart' show Client;
class ApiService {
final String baseUrl = '192.168.100.242:8080';
Client client = Client();
Future<List<Nasabah>> getNasabah() async {
final response = await client.get('http://$baseUrl/api/mstdebitur');
print(response.body);
if (response.statusCode == 200) {
final nasabah = (json.decode(response.body) as List)
.map((e) => Nasabah.fromMap(e))
.toList();
return nasabah;
} else {
throw Exception('Failed to load post');
}
}
Future<bool> createNasabah(Nasabah data) async {
String url = new Uri.http("$baseUrl", "/api/mstdebitur/").toString();
final response = await client.post(url,
headers: {"Content-Type": "application/json"},
body: nasabahToJson(data));
if (response.statusCode == 201) {
return true;
} else {
return false;
}
}
Future<bool> updateNasabah(Nasabah data) async {
String url =
new Uri.http("$baseUrl", "/api/mstdebitur/${data.id}").toString();
final response = await client.put(url,
headers: {"Content-Type": "application/json"},
body: nasabahToJson(data));
if (response.statusCode == 200) {
return true;
} else {
return false;
}
}
Future<bool> deleteNasabah(int id) async {
String url = new Uri.http("$baseUrl", "/api/mstdebitur/$id").toString();
final response = await client.delete(url);
if (response.statusCode == 200) {
return true;
} else {
return false;
}
}
}
Home
import 'package:flutter/material.dart';
import 'package:flutter_auth/Models/nasabah.dart';
import 'package:flutter_auth/network/nasabah_service.dart';
import 'FormAddNasabah.dart';
import 'ListNasabah.dart';
GlobalKey<ScaffoldState> _scaffoldState = GlobalKey<ScaffoldState>();
// ignore: must_be_immutable
class DataNasabah extends StatefulWidget {
DataNasabah({Key key}) : super(key: key);
String title;
#override
_DataNasabahState createState() => _DataNasabahState();
}
class _DataNasabahState extends State<DataNasabah> {
ApiService apiService;
ListNasabah _listNasabah;
List<Nasabah> data;
#override
void initState() {
super.initState();
apiService = ApiService();
_listNasabah = new ListNasabah(apiService: apiService);
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldState,
appBar: AppBar(
title: Text(
'Data Nasabah',
style: TextStyle(color: Colors.white),
),
actions: <Widget>[
GestureDetector(
onTap: () async {
var result = await Navigator.push(
_scaffoldState.currentContext,
MaterialPageRoute(builder: (BuildContext context) {
return FormAddNasabah(nasabah: null);
}),
);
if (result != null) {
setState(() {});
}
},
child: Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Icon(
Icons.add,
color: Colors.white,
),
),
)
],
),
body: _listNasabah.createViewList(),
);
}
}
List
import 'package:flutter/material.dart';
import 'package:flutter_auth/Models/nasabah.dart';
import 'package:flutter_auth/network/nasabah_service.dart';
import 'package:flutter_auth/screens/Menu/DataNasabah/FormAddNasabah.dart';
import 'package:flutter_auth/screens/Menu/DataNasabah/NasabahHome.dart';
import 'package:rflutter_alert/rflutter_alert.dart';
class ListNasabah {
ApiService apiService;
ListNasabah({this.apiService});
Widget createViewList() {
return SafeArea(
child: FutureBuilder(
future: apiService.getNasabah(),
builder: (BuildContext context, AsyncSnapshot<List<Nasabah>> snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(
'Something wrong with message: ${snapshot.error.toString()}',
textAlign: TextAlign.center,
),
);
} else if (snapshot.connectionState == ConnectionState.done) {
List<Nasabah> nasabah = snapshot.data;
return nasabahListView(nasabah);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
Widget nasabahListView(List<Nasabah> listnasabah) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
child: ListView.builder(
shrinkWrap: true,
itemBuilder: (context, index) {
Nasabah nasabah = listnasabah[index];
return Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
nasabah.nama_debitur,
style: Theme.of(context).textTheme.bodyText1,
),
Text(nasabah.alamat),
Text(nasabah.no_ktp),
Text(nasabah.no_telp),
Text(nasabah.no_selular),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FlatButton(
onPressed: () {
apiService.deleteNasabah(nasabah.id).then((value) =>
Navigator.of(context).pushNamed('start'));
},
child: Text(
"Hapus",
style: TextStyle(color: Colors.red),
),
),
FlatButton(
onPressed: () async {
var result = await Navigator.push(context,
MaterialPageRoute(builder: (context) {
return FormAddNasabah(
nasabah: nasabah,
);
}));
},
child: Text(
'Edit',
style: TextStyle(color: Colors.blue),
),
),
],
)
],
),
),
),
);
},
itemCount: listnasabah.length,
),
);
}
}
Form
import 'package:flutter/material.dart';
import 'package:flutter_auth/Models/nasabah.dart';
import 'package:flutter_auth/network/nasabah_service.dart';
import 'package:flutter_auth/screens/Menu/DataNasabah/NasabahHome.dart';
import 'package:rflutter_alert/rflutter_alert.dart';
class FormAddNasabah extends StatefulWidget {
Nasabah nasabah;
FormAddNasabah({this.nasabah});
#override
_FormAddNasabahState createState() => _FormAddNasabahState();
}
class _FormAddNasabahState extends State<FormAddNasabah> {
ApiService apiService;
TextEditingController _contNama = TextEditingController();
TextEditingController _contAlamat = TextEditingController();
TextEditingController _contNoTelp = TextEditingController();
TextEditingController _contNoKtp = TextEditingController();
TextEditingController _contNoSelular = TextEditingController();
#override
void initState() {
super.initState();
apiService = ApiService();
if (widget.nasabah != null) {
_contNama.text = widget.nasabah.nama_debitur;
_contAlamat.text = widget.nasabah.alamat;
_contNoTelp.text = widget.nasabah.no_telp;
_contNoKtp.text = widget.nasabah.no_ktp;
_contNoSelular.text = widget.nasabah.no_selular;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(color: Colors.white),
title: Text(
widget.nasabah == null ? "Tambah Nasabah" : "Edit Nasabah",
style: TextStyle(color: Colors.white),
),
),
body: Stack(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
_buildTextField(_contNama, "Nama Nasabah"),
_buildTextField(_contAlamat, "Alamat"),
_buildTextField(_contNoTelp, "No Telp"),
_buildTextField(_contNoKtp, "No KTP"),
_buildTextField(_contNoSelular, "No Selular"),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: RaisedButton(
child: Text(widget.nasabah == null ? "Tambah" : "Edit"),
color: Colors.blue,
onPressed: () {
int id = 0;
if (widget.nasabah != null) {
id = widget.nasabah.id;
}
Nasabah nasabah = Nasabah(
id: id,
nama_debitur: _contNama.text,
alamat: _contAlamat.text,
no_telp: _contNoTelp.text,
no_ktp: _contNoKtp.text,
no_selular: _contNoSelular.text);
if (widget.nasabah == null) {
apiService.createNasabah(nasabah);
} else {
apiService.updateNasabah(nasabah);
}
Navigator.of(context)
.pushNamed('start')
.whenComplete(() => Navigator.pop(context));
},
),
)
],
),
)
],
),
);
}
Widget _buildTextField(TextEditingController _cont, String label) {
return TextField(
controller: _cont,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: label,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
);
}
}
Thank you for your attention!
Problem :
Im trying to delete data from Flutter DataTable, when i pressed the alertdialog and click yes the Data already deleted on the Database but the data still shown in the screen until i click the back navigation button and back to list of user.
Expected :
The Datatable should automatically refreshed with current data minus the data that i deleted earlier.
Datatable
// ignore_for_file: deprecated_member_use
import 'package:flutter/material.dart';
import 'package:flutter_auth/Models/nasabah.dart';
import 'package:flutter_auth/network/nasabah_service.dart';
import 'package:flutter_auth/screens/Menu/DataNasabah/detailnasabah.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
class DataNasabah extends StatefulWidget {
#override
_DataNasabahState createState() => _DataNasabahState();
}
class _DataNasabahState extends State<DataNasabah> {
String nama_debitur = '';
List<Nasabah> _nasabah = [];
#override
void initState() {
super.initState();
_loadUserData();
_getNasabah();
}
_loadUserData() async {
SharedPreferences localStorage = await SharedPreferences.getInstance();
var user = jsonDecode(localStorage.getString('user'));
if (user != null) {
setState(() {
nama_debitur = user['nama_debitur'];
});
}
}
_getNasabah() {
NasabahService.getUser().then((nasabah) {
if (mounted) {
setState(() {
_nasabah = nasabah;
});
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text('Data Nasabah'),
// backgroundColor: Color(0xff151515),
// automaticallyImplyLeading: false,
),
body: SingleChildScrollView(
child: PaginatedDataTable(
rowsPerPage: 10,
header: Align(
alignment: Alignment.center,
child: Text(
'Data Nasabah',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
// Text("Data Nasabah"),
columns: [
DataColumn(
label: Expanded(
child: Text(
'ID Nasabah',
textAlign: TextAlign.center,
)),
),
DataColumn(
label: Expanded(
child: Text(
'Nama Nasabah',
textAlign: TextAlign.center,
)),
),
DataColumn(
label: Expanded(
child: Text(
'Aksi',
textAlign: TextAlign.center,
)),
),
],
source: NasabahDataTableSource(userData: _nasabah, context: context),
),
),
);
}
}
class NasabahDataTableSource extends DataTableSource {
BuildContext context;
NasabahDataTableSource({this.context, this.userData});
final List<Nasabah> userData;
#override
DataRow getRow(int index) {
return DataRow.byIndex(
index: index,
cells: [
DataCell(Align(
alignment: Alignment.center,
child: Text(
"${userData[index].id}",
))),
DataCell(Align(
alignment: Alignment.center,
child: Text("${userData[index].nama_debitur}"),
)),
DataCell(
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: Icon(Icons.navigate_next),
color: Colors.blueAccent,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailNasabah(
nasabah: userData[index],
),
),
);
},
),
IconButton(
icon: Icon(Icons.delete),
color: Colors.red,
onPressed: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Hapus Data Nasabah'),
content: Text(
'Apakah anda yakin ingin menghapus data nasabah ini?'),
actions: [
FlatButton(child: Text('Yes'), onPressed: () {
NasabahService.deleteUser(userData[index].id);
Navigator.pop(context);
})
],
),
);
},
)
],
),
),
// DataCell(
// FlatButton(
// child: Icon(
// Icons.chevron_right,
// color: Colors.red,
// ),
// onPressed: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => DetailNasabah(
// nasabah: userData[index],
// ),
// ),
// );
// },
// ),
// ),
],
);
}
#override
bool get isRowCountApproximate => false;
#override
int get rowCount => userData.length;
#override
int get selectedRowCount => 0;
void sort<T>(Comparable<T> getField(Nasabah d), bool ascending) {
userData.sort((Nasabah a, Nasabah b) {
if (!ascending) {
final Nasabah c = a;
a = b;
b = c;
}
final Comparable<T> aValue = getField(a);
final Comparable<T> bValue = getField(b);
return Comparable.compare(aValue, bValue);
});
notifyListeners();
}
}
Api
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
class Network {
final String _url = 'http://192.168.100.207:8080/api/';
// 192.168.1.2 is my IP, change with your IP address
var token;
_getToken() async {
SharedPreferences localStorage = await SharedPreferences.getInstance();
token = jsonDecode(localStorage.getString('token'));
}
auth(data, apiURL) async {
var fullUrl = _url + apiURL;
return await http.post(fullUrl,
body: jsonEncode(data), headers: _setHeaders());
}
getData(apiURL) async {
var fullUrl = _url + apiURL;
await _getToken();
return await http.get(
fullUrl,
headers: _setHeaders(),
);
}
deleteData(apiURL, id) async {
var fullUrl = _url + apiURL + '/' + id.toString();
await _getToken();
return await http.delete(
fullUrl,
headers: _setHeaders(),
);
}
_setHeaders() => {
'Content-type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token',
};
}
Service
import 'package:flutter_auth/Models/nasabah.dart';
import 'api.dart';
import 'dart:convert';
class NasabahService {
static String baseUrl = "mstdebitur";
static Future<List<Nasabah>> getUser() async {
final response = await Network().getData(baseUrl);
List<Nasabah> list = parseResponse(response.body);
return list;
}
static Future<List<Nasabah>> deleteUser(id) async {
final response = await Network().deleteData(baseUrl, id);
List<Nasabah> list = parseResponse(response.body);
return list;
}
static List<Nasabah> parseResponse(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Nasabah>((json) => Nasabah.fromJson(json)).toList();
}
}
How to add a stream with a picture(ImagePicker) in the bloc architecture, which can be selected from the phone, what should the widget look like?
My example is without validation whether it is to be included?
How should I work in a bloc along with the avatar photo?
I do not know how to approach it and what steps would be taken when it comes to both validation of such a photo, if possible, and the use of stream with bloc, any help is very welcome.
class ProfileView extends StatefulWidget {
const ProfileView({Key? key}) : super(key: key);
#override
_ProfileViewState createState() => _ProfileViewState();
static Route route() {
return MaterialPageRoute<void>(builder: (_) => ProfileView());
}
}
class _ProfileViewState extends State<ProfileView> {
final bloc = Bloc();
#override
Widget build(BuildContext context) {
return Scaffold(
body: _profilePage(context),
);
}
Widget _profilePage(BuildContext context) {
return ColorfulSafeArea(
color: orange,
child: Center(
child: Column(
children: [
_changeAvatarButton(context),
SizedBox(height: 15),
_usernameTile(),
SizedBox(height: 5),
_cityTile(),
SizedBox(height: 60),
],
),
),
);
// });
}
// Profile avatar
Widget _avatar() {
return CircleAvatar(
radius: 83,
backgroundColor: orange,
child: CircleAvatar(
backgroundColor: Colors.white,
radius: 80,
child: Icon(personIcon, size: 60, color: orange),
),
);
}
Widget _changeAvatarButton(BuildContext context) {
return Column(
children: [
CircleAvatar(
radius: 83,
backgroundColor: orange,
child: _image == null
? _avatar()
: ClipRRect(
borderRadius: BorderRadius.circular(83),
child: Image.file(
_image!,
height: 160,
// Change the size up or down accordingly border radius
width: 160,
// Change the size up or down accordingly border radius
fit: BoxFit.cover,
)),
),
CustomButtonText(
onPressed: () {
_showPicker(context);
},
title: changePhoto,
textColor: teal),
],
);
}
File? _image;
final picker = ImagePicker();
Future getImage() async {
final pickedFile = await picker.getImage(
source: ImageSource.gallery, maxWidth: 1800, maxHeight: 1800);
setState(() {
if (pickedFile != null) {
_image = File(pickedFile.path);
} else {
print(getImageText);
}
});
}
Future getCameraImage() async {
final pickedFile = await picker.getImage(
source: ImageSource.camera,
maxWidth: 1800,
maxHeight: 1800,
);
setState(() {
if (pickedFile != null) {
_image = File(pickedFile.path);
} else {
print(getImageText);
}
});
}
void _showPicker(BuildContext context) {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext bc) {
return SafeArea(
child: Container(
child: Wrap(
children: <Widget>[
ListTile(
leading: Icon(photoIcon),
title: Text(showPickerPhoto),
onTap: () async {
await getImage();
Navigator.of(context).pop();
}),
ListTile(
leading: Icon(cameraIcon),
title: Text(showPickerCamera),
onTap: () async {
await getCameraImage();
Navigator.of(context).pop();
},
),
],
),
),
);
});
}
Widget _usernameTile() {
return StreamBuilder(
stream: bloc.name,
builder: (context, snapshot) {
return CustomTextField(
title: 'Name',
obscureText: false,
colour: Colors.black,
keyboardType: TextInputType.name,
labelText: labelTextUsername,
onChanged: bloc.changeName,
errorText: snapshot.error != null ? 'invalid name' : null
// onChanged: (newValue) {
// bloc.changeName(newValue);
// },
);
});
}
Widget _cityTile() {
return StreamBuilder(
stream: bloc.city,
builder: (context, snapshot) {
return CustomTextField(
title: 'City',
obscureText: false,
colour: Colors.black,
keyboardType: TextInputType.name,
labelText: labelTextCity,
onChanged: bloc.changeCity,
errorText: snapshot.error != null ? 'invalid city' : null
);
});}
}
Validators looks in this way:
class Validators {
static final RegExp _nameRegExp = RegExp(
r'^(?=.*[a-z])[A-Za-z ]{3,}$',
);
final validateName =
StreamTransformer<String, String>.fromHandlers(handleData: (name, sink) {
if (name.contains(_nameRegExp)) {
sink.add(name);
} else {
sink.addError('Enter a valid name');
}
});
static final RegExp _cityRegExp = RegExp(
r'^[a-zA-Z]+(?:[\s-][a-zA-Z]+)*$',
);
final validateCity =
StreamTransformer<String, String>.fromHandlers(handleData: (city, sink) {
if (city.contains(_cityRegExp)) {
sink.add(city);
} else {
sink.addError('Enter a valid city');
}
});
static final RegExp _avatarRegExp = RegExp(
r'/.*\.(gif|jpe?g|bmp|png)$/igm',
);
final validateAvatar =
StreamTransformer<String, String>.fromHandlers(handleData: (avatar, sink) {
if (avatar.contains(_avatarRegExp)) {
sink.add(avatar);
} else {
sink.addError('Enter a valid avatar photo');
}
});
}
and Bloc:
import 'dart:async';
import 'dart:io';
import 'validators.dart';
class Bloc extends Validators {
//instances
//
final _avatarPath = StreamController<File>();
final _name = StreamController<String>();
final _city = StreamController<String>();
//add data stream
Stream<File> get avatarPath => _avatarPath.stream;
Stream<String> get name => _name.stream.transform(validateName);
Stream<String> get city => _city.stream.transform(validateCity);
// change data
Function(File) get changeAvatarPath => _avatarPath.sink.add;
Function(String) get changeName => _name.sink.add;
Function(String) get changeCity => _city.sink.add;
//for cleanup
void dispose() {
_avatarPath.close();
_name.close();
_city.close();
}
}
// bloc.changeName---> bloc.nameController.sin.add
final bloc = new Bloc();