Flutter Dynamic Searching appBar - flutter

I'm trying to create a dynamic search in the appBar, but unfortunately I'm not succeeding and I have an error on this line.
results = _listPlaces.where((places) => places["place"].toLowerCase().contains(enteredKeyword.toLowerCase())
Full code:
import 'package:favspot/src/views/places_card.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import '../../services/places.dart';
import 'dart:async';
class SeachPlaces extends StatefulWidget {
#override
State<SeachPlaces> createState() => _SeachPlacesState();
}
class _SeachPlacesState extends State<SeachPlaces> {
List<Object> _listPlaces = [];
List<Object> _searchSearch = [];
#override
initState(){
_searchSearch = _listPlaces;
super.initState();
}
void _runFilter(String enteredKeyword) {
List<Object> results = [];
if (enteredKeyword.isEmpty) {
results = _listPlaces;
} else {
results = _listPlaces.where((places) => places["place"].toLowerCase().contains(enteredKeyword.toLowerCase())
).toList();
setState((){
_searchSearch = results;
});
}
}
#override
void didChangeDependencies() {
super.didChangeDependencies();
getPlacesList();
}
TextEditingController? _textEditngController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Container(
width: double.infinity,
height: 40,
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(30)),
child: Center(
child:TextField(
onChanged: (value) => _runFilter(value),
controller: _textEditngController,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(5),
prefixIcon: Icon(Icons.search),
suffix: IconButton(
icon: Icon(Icons.clear),
onPressed: null,
),
hintText: 'Searching...',
border: InputBorder.none,
),
),
),
),
),
body: SafeArea(
child: ListView.builder(
itemCount: _searchSearch.length,
itemBuilder: (context, index) {
return PlacesCard(_searchSearch[index] as Places);
},
)
),
);
}
Future getPlacesList() async {
var data = await FirebaseFirestore.instance
.collection('places')
.orderBy('city', descending: false)
.get();
setState(() {
_listPlaces = List.from(data.docs.map((doc) => Places.fromSnapshot(doc)));
});
}
}
Here is de class Places
class Places {
String? place;
String? city;
String? status;
Places();
Map<String, dynamic> toJson() => {
'city' : city,
'status' : status,
};
Places.fromSnapshot(snapshot) :
place = snapshot.id,
city = snapshot.data()['city'],
status = snapshot.data()['status'];
}

Consider to use List<Places> _listPlaces instead of List<Object> _listPlaces, so you can access properties like this:
results = _listPlaces.where((places) => places.place != null && places.place!.toLowerCase().contains(enteredKeyword.toLowerCase())
).toList();

Related

the variable date of DateTime doesn't save when I press on cold reload

I used Share_Prefereces library so I want to save date of adding Item. I tried many ways but
I always get the errors: E/flutter ( 2786): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'Null' is not a subtype of type 'DateTime'
and the second error is: Unhandled Exception: Converting object to an encodable object failed: Instance of 'Shopping'
so please help me
the code of Date file is:
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
class Shopping {
String item;
int price;
DateTime date;
Shopping({required this.item, required this.price, required this.date});
Map<String, dynamic> toJson() {
return {
'item': item,
'price': price,
'date': date,
};
}
}
class ItemData extends ChangeNotifier {
List<Shopping> listOfItem = [];
void addItem(Shopping shopping) {
listOfItem.add(shopping);
notifyListeners();
}
void editItem(Shopping shopping, int itemIndex) {
listOfItem[itemIndex] = shopping;
notifyListeners();
}
void deleteItem(int itemIndex) {
listOfItem.removeAt(itemIndex);
notifyListeners();
}
saveData() async {
SharedPreferences pref = await SharedPreferences.getInstance();
List<String> tempList = [];
for (int i = 0; i < listOfItem.length; i++) {
tempList.add(jsonEncode(listOfItem[i]));
}
pref.remove("itemList");
pref.setStringList("itemList", tempList);
}
loadData() async {
SharedPreferences pref = await SharedPreferences.getInstance();
if (pref.getStringList('itemList') != null) {
List<String> tempList = pref.getStringList('itemList')!;
for (int i = 0; i < tempList.length; i++) {
Map<String, dynamic> temp = jsonDecode(tempList[i]);
addItem(
Shopping(
item: temp['item'],
price: temp['price'],
date: temp['date'],
),
);
}
}
}
}
and the code of adding Item, price and date is:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:goods_and_price/shopping_data.dart';
import 'package:provider/provider.dart';
class AddItem extends StatelessWidget {
AddItem({Key? key}) : super(key: key);
TextEditingController userInputItem = TextEditingController();
TextEditingController userInputPrice = TextEditingController();
#override
Widget build(BuildContext context) {
var provider = Provider.of<ItemData>(context, listen: true);
DateTime date = DateTime.now();
return Scaffold(
appBar: AppBar(
title: const Text('Add Item'),
centerTitle: true,
backgroundColor: const Color(0xFF00899C),
),
body: ListView(
physics: const BouncingScrollPhysics(),
children: [
Padding(
padding: const EdgeInsets.all(10.0),
child: TextField(
controller: userInputItem,
decoration: InputDecoration(
hintText: 'Item',
labelText: 'Item',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
prefixIcon: const Icon(
Icons.shopping_cart,
color: Color(0xFF00899C),
)),
maxLines: null,
),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: TextField(
controller: userInputPrice,
decoration: InputDecoration(
hintText: 'Price',
labelText: 'Price',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
prefixIcon: const Icon(
Icons.attach_money,
color: Color(0xFF00899C),
)),
maxLines: null,
),
),
const SizedBox(
height: 10,
),
CupertinoButton(
padding: const EdgeInsets.all(0),
pressedOpacity: 0.5,
child: Container(
height: 50,
width: 120,
decoration: BoxDecoration(
color: const Color(0xFF00899C),
borderRadius: BorderRadius.circular(10)),
child: const Center(
child: Text(
'Add Item',
style: TextStyle(color: Colors.white),
),
),
),
onPressed: () async {
Shopping newItem = Shopping(
item: userInputItem.text,
price: int.parse(userInputPrice.text),
date: date,
);
provider.addItem(newItem);
provider.saveData();
Navigator.pop(context);
},
),
],
),
);
}
}
I don't recommend using DateTime, because it takes a certain amount of work to use in different languages, I find it easier to use String.
I couldn't simulate the error on my machine, but I made an example, I hope it helps.
To create class I like to use this site https://javiercbk.github.io/json_to_dart/
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:zoociadoleite_app/global/my_app.dart';
class TesteScreen extends StatefulWidget {
const TesteScreen({Key key}) : super(key: key);
#override
State<TesteScreen> createState() => _TesteScreenState();
}
class Shopping {
String item;
int price;
String date;
Shopping({this.item, this.price, this.date});
Shopping.fromJson(Map<String, dynamic> json) {
item = json['item'];
price = json['price'];
date = json['date'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['item'] = this.item;
data['price'] = this.price;
data['date'] = this.date;
return data;
}
}
class LocalStorage {
Future<String> get(String item) async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString(item);
}
Future<bool> set(String item, dynamic data) async {
final prefs = await SharedPreferences.getInstance();
return prefs.setString(item, json.encode(data));
}
Future<bool> remove(String item) async {
final prefs = await SharedPreferences.getInstance();
return prefs.remove(item);
}
}
class _TesteScreenState extends State<TesteScreen> {
final formatDate = new DateFormat.yMMMMd('pt_BR');
Future<List<Shopping>> _future;
List<Shopping> list = [];
load() async {
String data = await LocalStorage().get("Shopping");
if (data != null) {
List<dynamic> lista = json.decode(data) as List;
list = lista.map((shopping) => Shopping.fromJson(shopping)).toList();
}
_future = Future.value(list);
setState(() {});
}
#override
void initState() {
super.initState();
load();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<List<Shopping>>(
future: _future,
builder: (context, snapshot) {
if (snapshot.hasData)
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
Shopping shopping = snapshot.data[index];
return ListTile(
title: Text(shopping.item),
subtitle: Text("${shopping.price}"),
trailing: Text("${formatDate.format(DateTime.parse(shopping.date))}"),
);
},
);
return CircularProgressIndicator();
},
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
list.add(Shopping(item: "test_${list.length + 1}", price: list.length + 1, date: DateTime.now().toString()));
_future = Future.value(list);
LocalStorage().set("Shopping", list);
setState(() {});
},
child: const Icon(Icons.add),
),
);
}
}

How can I add data with objectBox?

The data must be consumed from the ObjectBox's database and loaded into the dependent lists. which is already being done.
I have to add an "Add Region" button that already has the function that sends me to the other view to add a region and communicate with the ObjectBox's database. Which is being done, but not how I would like it to be, since the idea is to add the Region with an auto-incrementing id and the communes are associated with that id when added.
When the data is already added, it should automatically sync or have a button that does that sync job
Controller
class HomeController extends GetxController {
var regionList = <RegionModel>[].obs;
List<ComunaModel> comunas = <ComunaModel>[].obs;
List<RegionModel> get regiones => regionList.value;
final dropdownRegiones = [].obs;
final dropdownComunas = <String>[].obs;
#override
void onInit() {
regionList.bindStream(objectbox.getRegiones());
}
}
ComunaModel
#Entity()
class ComunaModel {
#Id()
int comunaId = 0;
late String comunaNombre;
final region = ToOne<RegionModel>();
ComunaModel({this.comunaId = 0, required this.comunaNombre});
}
RegionModel
#Entity()
class RegionModel {
#Id()
int regionId = 0;
late String regionNombre;
late int regionCodigo;
#Backlink()
final comunas = ToMany<ComunaModel>();
RegionModel(
{this.regionId = 0,
required this.regionNombre,
required this.regionCodigo});
}
ObjectBox
class ObjectBox {
late final Store store;
late final Admin admin;
late final Box<RegionModel> regionBox;
late final Box<ComunaModel> comunaBox;
ObjectBox._create(this.store) {
if (Admin.isAvailable()) {
admin = Admin(store);
}
regionBox = Box<RegionModel>(store);
comunaBox = Box<ComunaModel>(store);
if (regionBox.isEmpty()) {
_putData();
}
}
get regionController => null;
get comunaController => null;
/// Create an instance of ObjectBox to use throughout the app.
static Future<ObjectBox> create() async {
// Note: on desktop systems this returns the users documents directory,
// so make sure to create a unique sub-directory.
// On mobile using the default (not supplying any directory) is typically
// fine, as apps have their own directory structure.
final documentsDirectory = await getApplicationDocumentsDirectory();
final databaseDirectory =
p.join(documentsDirectory.path, "objectbox example");
// Future<Store> openStore() {...} is defined in the generated objectbox.g.dart
final store = await openStore(directory: databaseDirectory);
return ObjectBox._create(store);
}
void _putData() {
RegionModel region =
RegionModel(regionNombre: 'Arica y Parinacota', regionCodigo: 1);
ComunaModel comuna = ComunaModel(comunaNombre: 'Arica');
region.comunas.add(comuna);
comuna = ComunaModel(comunaNombre: 'Camarones');
region.comunas.add(comuna);
comuna = ComunaModel(comunaNombre: 'General Lagos');
region.comunas.add(comuna);
comuna = ComunaModel(comunaNombre: 'Putre');
region.comunas.add(comuna);
regionBox.put(region);
//final customerId = store.box<RegionModel>().put(region);
}
Stream<List<RegionModel>> getRegiones() {
final qBuilderRegiones = regionBox.query()
..order(RegionModel_.regionId, flags: Order.descending);
return qBuilderRegiones
.watch(triggerImmediately: true)
.map((query) => query.find());
}
Future<void> addRegionNombre(String regionNombre, int regionCodigo) async {
RegionModel region =
RegionModel(regionNombre: regionNombre, regionCodigo: regionCodigo);
regionBox.put(region);
}
Future<void> addComunaNombre(String comunaNombre) async {
ComunaModel comuna = ComunaModel(comunaNombre: comunaNombre);
comunaBox.put(comuna);
}
}
HomePage
class HomePage extends GetView<HomeController> {
const HomePage({super.key});
#override
Widget build(BuildContext context) {
var title = "DropDownSearch Con ObjectBox";
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(26),
child: StreamBuilder<List<RegionModel>>(
stream: objectbox.getRegiones(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: [
Obx(
() {
HomeController controller = Get.put(HomeController());
return controller.regionList.isEmpty
? const Center(
child: Text('No hay regiones'),
)
: DropdownSearch<RegionModel>(
popupProps:
const PopupProps.menu(showSearchBox: true),
asyncItems: (String filter) async {
return controller.regionList;
},
itemAsString: (RegionModel u) => u.regionNombre,
onChanged: (RegionModel? data) {
if (data?.comunas != null &&
data!.comunas.isNotEmpty) {
controller.comunas = data.comunas;
print(controller.comunas
.map((e) => e.comunaNombre)
.toList());
}
controller.dropdownComunas.value = controller
.comunas
.map((e) => e.comunaNombre)
.toList();
},
dropdownDecoratorProps:
const DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
labelText: "Region",
hintText: "Seleccione una region",
),
),
);
},
),
Obx(
() => DropdownSearch<String>(
popupProps: const PopupProps.menu(
showSelectedItems: true,
),
items: controller.dropdownComunas.value,
dropdownDecoratorProps: const DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
labelText: "Comuna",
hintText: "Seleccione una comuna",
),
),
onChanged: print,
selectedItem: controller.comunas.isNotEmpty == true
? controller.comunas[0].comunaNombre
: null),
),
Container(
padding: const EdgeInsets.all(100),
child: Align(
alignment: Alignment.bottomRight,
child: Column(children: [
FloatingActionButton.extended(
key: const Key('add'),
label: const Text('Add Region'),
heroTag: null,
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => const AddPage()));
},
),
]),
),
),
],
);
} else {
return const CircularProgressIndicator();
}
},
),
),
),
);
}
}
AddPage
class AddPage extends StatefulWidget {
const AddPage({super.key});
#override
State<AddPage> createState() => _AppPageState();
}
class _AppPageState extends State<AddPage> {
#override
Widget build(BuildContext context) {
const appTitle = "Agregar Region";
return // scaffold
Scaffold(
appBar: AppBar(
title: const Text(appTitle),
),
body: Padding(
padding: const EdgeInsets.all(26),
child: Column(
children: [
TextFormField(
decoration: const InputDecoration(
labelText: "Region",
hintText: "Ingrese una region",
),
onFieldSubmitted: (String value) {
objectbox.addRegionNombre(value, 1);
},
onEditingComplete: () {
Get.snackbar("Region", "Region agregada");
},
),
TextFormField(
decoration: const InputDecoration(
labelText: "Comuna",
hintText: "Ingrese una comuna",
),
onFieldSubmitted: (String value) {
objectbox.addComunaNombre(value);
},
onEditingComplete: () {
Get.snackbar("Comuna", "Comuna agregada");
},
),
],
),
),
);
}
}
Update: I created this method so that it will add region and commune, but it doesn't work
Future<List<int>> addRegionNombre(
String regionNombre, int regionCodigo) async {
RegionModel region =
RegionModel(regionNombre: regionNombre, regionCodigo: regionCodigo);
final regionId = regionBox.put(region);
List<int> regionComunaId = [];
regionComunaId.add(regionId);
for (var i = 0; i < region.comunas.length; i++) {
region.comunas[i].comunaId = regionId;
final comunaId = comunaBox.put(region.comunas[i]);
regionComunaId.add(comunaId);
}
return regionComunaId;
}
You can achieve this by using a stream to keep track of the latest state of the database and feeding that into a ListView builder.
You can take a look at this code example to see how I do that:
Setting up Stream inside ObjectBox Class
Generating a List using that stream

Flutter Barcode Scan Result Is'nt Listed

My application is to search through the list of books. Two different variables (book name or barcode) can be used while searching. There is no problem when searching by name. but when searching with barcode scanning, no results are listed. When I type the barcode manually, the application still works without any problems.
Can u help me?
Manually entered barcode: https://i.stack.imgur.com/njtLA.png
Barcode scan result : https://i.stack.imgur.com/ZsGot.png
My code here..
import 'package:fff/book_tile.dart';
import 'package:flutter/material.dart';
import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:fff/book_model.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
TextEditingController _controller = new TextEditingController();
List<Book> _booksForDisplay = [];
List<Book> _books = [];
#override
void initState() {
super.initState();
fetchBooks().then((value) {
setState(() {
_books.addAll(value);
_booksForDisplay = _books;
print(_booksForDisplay.length);
});
});
}
Future _scan(BuildContext context) async {
String barcode = await FlutterBarcodeScanner.scanBarcode(
'#ff0000',
'İptal',
true,
ScanMode.BARCODE
);
_controller.text = barcode;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
toolbarHeight: 80,
title: Padding(
padding: EdgeInsets.all(8),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(40)
),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
child: TextFormField(
textAlignVertical: TextAlignVertical.center,
controller: _controller,
decoration: InputDecoration(
border: InputBorder.none,
icon: Icon(Icons.search),
suffixIcon: IconButton(
icon: Icon(FontAwesomeIcons.barcode),
onPressed: (){
_scan(context);
},
)
),
onChanged: (string){
string = string.toLowerCase();
setState(() {
_booksForDisplay = _books.where((b){
var bName = b.name!.toLowerCase();
var bBarcode = b.barcode!.toLowerCase();
return bName.startsWith(string) || bBarcode.startsWith(string);
}).toList();
});
},
),
),
),
),
),
body: SafeArea(
child: Container(
child: _controller.text.isNotEmpty ? new ListView.builder(
itemCount: _booksForDisplay.length,
itemBuilder: (context, index){
return BookTile(book: this._booksForDisplay[index]);
},
)
:
Center(
child: Text('Searching..'),
)
),
)
);
}
}
I think you only need a listener for your TextEditingController. And you should write your onChanged method inside that listener.
#override
void initState() {
super.initState();
fetchBooks().then((value) {
setState(() {
_books.addAll(value);
_booksForDisplay = _books;
print(_booksForDisplay.length);
});
});
_controller.addListener(() {
print(_controller.text);
var string = _controller.text.toLowerCase();
setState(() {
_booksForDisplay = _books.where((b){
var bName = b.name!.toLowerCase();
var bBarcode = b.barcode!.toLowerCase();
return bName.startsWith(string) ||
bBarcode.startsWith(string);
}).toList();
});
});
}

In flutter, json data is not appearing in listview initially

At first, the list of publications was shown in ListTile. However, when I changed the String query, it doesn't show any data at all, and I can't search it. I want to show it as a ListView, but I want to hide it before searching and enter the correct title when I search, and I want to see what I searched. It's strange why suddenly a JSON file comes out.
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(MaterialApp(
home: MyGetHttpData(),
));
}
class MyGetHttpData extends StatefulWidget {
#override
MyGetHttpDataState createState() => MyGetHttpDataState();
}
class MyGetHttpDataState extends State<MyGetHttpData> {
String query = '유시민';
int page = 1;
String url;
List data;
Future<String> getJSONData() async {
var response = await http.get(
Uri.encodeFull(url),
headers: {"Accept": "application/json" , 'Authorization' : 'KakaoAK 5a1167ba90ccb4b79d84ef5a41da0fbd'});
//REST API 여기에 입력//
print(response.body);
setState(() {
var dataConvertedToJSON = json.decode(response.body);
data = dataConvertedToJSON['documents'];
});
return "Successfull";
}
TextEditingController editingController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('widgettitle'),
),
body: Container(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
onChanged: (value) {
},
controller: editingController,
decoration: InputDecoration(
labelText: "Search",
hintText: "Search",
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25.0)))),
),
),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: data == null ? 0 : data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(
data[index]['publisher'].toString(),
style: TextStyle(
fontSize: 20.0, color: Colors.black, ),
),
);
},
),
),
],
),
),
);
}
}
SharedPreferences prefs;
List<String> list;
// ...
Future<bool> _saveList() async {
return await prefs.setStringList("key", list);
}
List<String> _getList() {
return prefs.getStringList("key");
}
#override
void initState() {
String query = '유시민';
int page = 1;
String url;
List data;
url = 'https://dapi.kakao.com/v3/search/book?sort=accuracy&target=title&size=10&query= $query &page= $page';
}

Flutter Auth (BLoC pattern & rxDart)

I wanted to make applications with authorization on the BLoC partner, but I encountered an error:
The following NoSuchMethodError was thrown building AuhtScreen(dirty, state: _AuhtScreenState<dynamic>#00539):
The getter 'blocState' was called on null.
Receiver: null
Tried calling: blocState
It is called under the following circumstances:
In AuhtScreen
AuthBloc authBloc = BlocProvider.of(context).authBloc; (context = StatefulElement)
In BlocProvider
static BlocState of(BuildContext context) { (context = StatefulElement)
return (context.inheritFromWidgetOfExactType(BlocProvider) as BlocProvider).blocState; (context = StatefulElement)(NULL)
}
I do not understand why it does not work, I do everything correctly, maybe I missed something or did not understand... Help solve the problem!
All code:
AuthBloc
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:mifity/models/auht_detail.dart';
import 'package:mifity/models/user.dart';
import 'package:mifity/screens/main_screen.dart';
import 'package:mifity/services/auth_service.dart';
import 'package:rxdart/rxdart.dart';
class AuthBloc {
AuthService authService;
BuildContext _context;
final currentUserSubject = BehaviorSubject<User>.seeded(null);
final emailSubject = BehaviorSubject<String>.seeded('');
final passwordSubject = BehaviorSubject<String>.seeded('');
final loadingSubject = BehaviorSubject<bool>.seeded(false);
final loginSubject = BehaviorSubject<Null>.seeded(null);
//sink
void Function(String) get emailChanged => emailSubject.sink.add;
void Function(String) get passwordChanged => passwordSubject.sink.add;
void Function(BuildContext) get submitLogin => (context) {
this.setContext(context);
loginSubject.add(null);
};
//stream
Stream<User> get currentUser => currentUserSubject.stream;
Stream<String> get emailStream => emailSubject.stream;
Stream<String> get passwordStream => passwordSubject.stream;
Stream<bool> get loading => loadingSubject.stream;
AuthBloc({this.authService}) {
Stream<AuhtDetail> auhtDetailStream = Observable.combineLatest2(
emailStream, passwordStream, (email, password) {
return AuhtDetail(email: email, password: password);
});
Stream<User> loggedIn = Observable(loginSubject.stream)
.withLatestFrom(auhtDetailStream, (_, auhtDetail) {
return auhtDetail;
}).flatMap((auhtDetail) {
return Observable.fromFuture(authService.loginUser(auhtDetail))
.doOnListen(() {
loadingSubject.add(true);
}).doOnDone(() {
loadingSubject.add(false);
});
});
loggedIn.listen((User user) {
currentUserSubject.add(user);
Navigator.push(
_context,
new MaterialPageRoute(builder: (context) => MainScreen()),
);
}, onError: (error) {
Scaffold.of(_context).showSnackBar(new SnackBar(
content: new Text("Username or password incorrect"),
));
});
}
setContext(BuildContext context) {
_context = context;
}
close() {
emailSubject.close();
passwordSubject.close();
loadingSubject.close();
loginSubject.close();
}
}
BlocProvider
import 'package:flutter/material.dart';
import 'package:mifity/blocs/auth_bloc.dart';
import 'package:mifity/services/auth_service.dart';
class BlocProvider extends InheritedWidget {
final blocState = new BlocState(
authBloc: AuthBloc(authService: AuthService()),
);
BlocProvider({Key key, Widget child}) : super(key: key, child: child);
bool updateShouldNotify(_) => true;
static BlocState of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(BlocProvider) as BlocProvider)
.blocState;
}
}
class BlocState {
final AuthBloc authBloc;
BlocState({this.authBloc});
}
AuthService
import 'dart:async';
import 'package:mifity/models/auht_detail.dart';
import 'package:mifity/models/error.dart';
import 'package:mifity/models/user.dart';
class AuthService {
Future<User> loginUser(AuhtDetail detail) async {
await Future.delayed(Duration(seconds: 1)); //simulate network delay
if (detail.email == 'johndoe#acme.com' && detail.password == '1234') {
return User(
id: 1,
name: 'John Doe',
email: 'johndoe#acme.com',
age: 26,
profilePic: 'john_doe.png');
} else {
throw ClientError(message: 'login details incorrect.');
}
}
}
Validator:
class Validator {
String validateEmail(String value) {
if (value.isEmpty) return 'Email Should not be empty';
final RegExp emailRegEx = new RegExp(r'^\w+#[a-zA-Z_]+?\.[a-zA-Z]{2,3}$');
if (!emailRegEx.hasMatch(value)) return 'Your Email is invalid';
return null;
}
String validatePassword(String value) {
if (value.length < 4) return 'Password should be four characters or more';
return null;
}
}
AuhtScreen
import 'package:flutter/material.dart';
import 'package:mifity/blocs/auth_bloc.dart';
import 'package:mifity/blocs/bloc_provider.dart';
import 'package:mifity/helpers/validators.dart';
class AuhtScreen extends StatefulWidget {
#override
_AuhtScreenState createState() => _AuhtScreenState();
}
class _AuhtScreenState<StateClass> extends State<AuhtScreen> {
TextEditingController emailController = TextEditingController();
TextEditingController passwordController = TextEditingController();
Validator validator = new Validator();
final formKey = GlobalKey<FormState>();
DecorationImage backgroundImage = new DecorationImage(
image: new ExactAssetImage('assets/images/bg_image.jpg'),
fit: BoxFit.cover,
);
#override
Widget build(BuildContext context) {
AuthBloc authBloc = BlocProvider.of(context).authBloc;
final Size screenSize = MediaQuery.of(context).size;
return Scaffold(
appBar: AppBar(
title: Text('Login'),
),
body: Builder(builder: (context) {
return SingleChildScrollView(
child: Container(
height: screenSize.height - AppBar().preferredSize.height,
padding: EdgeInsets.all(10.0),
alignment: Alignment.center,
decoration: BoxDecoration(
image: (backgroundImage != null) ? backgroundImage : null),
child: Center(
child: Form(
key: formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
style: TextStyle(color: Colors.white),
controller: emailController,
decoration: InputDecoration(
labelText: 'email',
labelStyle: TextStyle(color: Colors.grey)),
validator: validator.validateEmail,
),
TextFormField(
style: TextStyle(color: Colors.white),
controller: passwordController,
decoration: InputDecoration(
labelText: 'password',
labelStyle: TextStyle(color: Colors.grey)),
obscureText: true,
validator: validator.validatePassword,
),
SizedBox(
height: 20.0,
),
StreamBuilder<bool>(
initialData: false,
stream: authBloc.loading,
builder: (context, loadingSnapshot) {
return SizedBox(
width: double.infinity,
child: RaisedButton(
color: Colors.deepOrange,
textColor: Colors.white,
child: Text((loadingSnapshot.data)
? 'Login ...'
: 'Login'),
onPressed: () {
_submit(context, authBloc);
},
),
);
},
),
],
),
),
),
),
);
}));
}
_submit(context, AuthBloc authBloc) {
authBloc.emailChanged(emailController.text);
authBloc.passwordChanged(passwordController.text);
if (formKey.currentState.validate()) {
authBloc.submitLogin(context);
}
}
}
I'm an idiot!)
MyApp:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocProvider(
child: MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new AuhtScreen(),
));
}
}