Flutter - Find cards by names - flutter

I am new to flutter and I have a program that shows several cards and I have a question about how to make a card finder, I am using this code:
_card(
String phrase,
) {
return SliverToBoxAdapter(
child: Card(
margin: EdgeInsets.only(right: 50, left: 50, top: 20),
child: InkWell(
onTap: () {},
child: Column(children: <Widget>[
SizedBox(height: 15.0),
Padding(
padding: EdgeInsets.only(left: 15, right: 15),
child: Text(
phrase,
style: TextStyle(
fontFamily: 'Circular',
fontSize: 17.0,
color: Colors.grey[800]),
),
),
SizedBox(height: 15.0),
]),
),
),
);
}
and I use this to make the various cards:
return Scaffold(
body: Stack(children: [
CustomScrollView(physics: BouncingScrollPhysics(), slivers: <Widget>[
_card('Abrir'),
_card('Alzar'),
_card('Aprender'),
_card('Caer'),
_card('Cerrar'),
_card('Cocinar'),
_card('Correr'),
_card('Cortar'),
_card('Enseñar'),
_card('Estar'),
_card('Hay'),
_card('Levantarse'),
_card('Mirar'),
_card('Oler'),
_card('Saltar'),
_card('Sentar'),
_card('Ser'),
_card('Tocar'),
_card('Tomar'),
_card('Tropezar'),
]),
]),
);
I really appreciate any help, thanks

Here is a solution using:
hooks_riverpod for State Management
fuzzy for fuzzy search
Full source code for easy copy-paste
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fuzzy/fuzzy.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
void main() {
runApp(
ProviderScope(
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: HomePage(),
),
),
);
}
class HomePage extends HookWidget {
#override
Widget build(BuildContext context) {
final phrases = useProvider(filteredPhrasesProvider);
return Scaffold(
body: ListView(
physics: BouncingScrollPhysics(),
children: [
TextField(
autofocus: true,
textAlignVertical: TextAlignVertical.center,
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
hintText: 'Search',
),
onChanged: (value) =>
context.read(searchTermsProvider).state = value,
),
...phrases.map((phrase) => _Card(phrase: phrase)).toList(),
],
),
);
}
}
class _Card extends StatelessWidget {
final String phrase;
const _Card({
Key key,
this.phrase,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.all(10.0),
child: InkWell(
onTap: () {},
child: Padding(
padding: EdgeInsets.all(15.0),
child: Text(
phrase,
style: TextStyle(
fontFamily: 'Circular',
fontSize: 17.0,
color: Colors.grey[800],
),
),
),
),
);
}
}
final searchTermsProvider = StateProvider<String>((ref) => '');
final phrasesProvider = Provider<List<String>>(
(ref) => [
'Abrir',
'Alzar',
'Aprender',
'Caer',
'Cerrar',
'Cocinar',
'Correr',
'Cortar',
'Enseñar',
'Estar',
'Hay',
'Levantarse',
'Mirar',
'Oler',
'Saltar',
'Sentar',
'Ser',
'Tocar',
'Tomar',
'Tropezar',
],
);
final filteredPhrasesProvider = Provider<List<String>>((ref) {
final phrases = ref.watch(phrasesProvider);
final searchTerms = ref.watch(searchTermsProvider).state;
return searchTerms.isEmpty
? phrases
: Fuzzy<String>(phrases, options: FuzzyOptions(threshold: .4))
.search(searchTerms)
.map((result) => result.item)
.toList();
});

First you must change the logic of your code, create a List and then create the cards, so that the search engine works with the list
Create list:
final List<String> actions = ["Abrir", "Alzar", "Enseñar", "Sentar", "Mirar"];
Next, use List.generate or List.builder to create cards in the slivers
return Scaffold(
body: Stack(children: [
CustomScrollView(
physics: BouncingScrollPhysics(),
slivers: List.generate(actions.length, (i) => _cards(actions[i])
),
]),
);
Finally in your seacher, use this logic, the "contains" is optional, you can change the logic in the if
void search(String data) {
for(int i = 0; i < actions.length; i++) {
if(actions[i].contains(data)) {
print(actions[i]);
// In your case show card or add in another list to show after
}
}
}

Related

flutter transfer data (color) to create a new widget

I'm creating a calendar app. The problem that I'm now facing is that I want to create a new user of the calendar. The user has the properties (which are now important) image, name and color.
I created a new File For the property color, in which the color can be changed. But I don't know how I can transfer the new color in the other file, so that I can use it to create the user.
I think it is possible to use the Material page route, but perhaps there is a more elegant way to handle this.
Does someone have an idea to handle this in a easy way?
UserSetScreen:
import 'package:calendar_vertical/screens/users_show_screen.dart';
import 'package:calendar_vertical/widgets/color_choose.dart';
import 'package:calendar_vertical/widgets/image_input.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class UserSetScreen extends StatefulWidget {
static const routeName = '/userSetScreen';
#override
State<UserSetScreen> createState() => _UserSetScreenState();
}
class _UserSetScreenState extends State<UserSetScreen> {
final _titleController = TextEditingController();
static const values = <String>[
'Administrator',
'normaler Nutzer',
'eingeschränkter Nutzer'
];
String selectedValue = values.first;
void _saveValues(User user) {
final neuerNutzer = User(
id: DateTime.now().toString(),
name: _titleController.text,
color: Colors.amber,
setAppointments: false,
administrator: false,
);
}
#override
Widget build(BuildContext context) {
final colorData = Provider.of<ColorChoose>(context);
return Scaffold(
appBar: AppBar(
title: Text('Person hinzufügen'),
actions: [
IconButton(
onPressed: () {
Navigator.of(context).pushNamed(UsersShowScreen.routeName);
},
icon: Icon(Icons.people),
),
],
),
body: Column(
children: [
Center(
child: ImageInput(),
),
Expanded(
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(10),
child: Column(
children: [
TextField(
decoration: InputDecoration(labelText: 'Name'),
controller: _titleController,
),
ColorChoose(),
//CheckboxListTile(
// value: value,
// onChanged: (value) => setState(() => this.value = value!),
// title: Text('Administrator'),
// controlAffinity: ListTileControlAffinity.leading,
//)
],
),
),
))
],
),
);
}
ColorChoose:
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
class ColorChoose extends StatefulWidget {
#override
State<ColorChoose> createState() => _ColorChooseState();
}
class _ColorChooseState extends State<ColorChoose> {
Color currentColor = Colors.white;
#override
Widget build(BuildContext context) {
return Row(
children: [
Text('Farbe: '),
Container(
decoration: BoxDecoration(
color: currentColor,
borderRadius: BorderRadius.all(
Radius.circular(15),
),
),
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0),
margin: EdgeInsets.only(left: 10.0),
),
Spacer(),
ElevatedButton(
onPressed: () => _showColorPicker(context),
child: Text(
'Farbe ändern',
),
),
],
);
}
void _showColorPicker(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Farbe wählen'),
titlePadding: const EdgeInsets.all(0.0),
contentPadding: const EdgeInsets.all(0.0),
content: SingleChildScrollView(
child: Wrap(
children: [
Container(
width: 300,
height: 300,
child: BlockPicker(
pickerColor: currentColor,
onColorChanged: (color) => setState(
() => this.currentColor = color,
),
),
)
],
),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('Close'),
)
],
),
);
}
}
Thank you very much.
Best regards
Patrick
I guess the best variant is to use GetX or another state manager.
Another way - to choose color right from the user screen, showing a dialog.
Finally you can pass valuenotifier to your color ColorChoose widget.

I'm having issues using Shared Preferences on another page

I have been having issues retrieving the "username" String saved in Shared Preferences (from the Settings page) for the home page text field (~Line 60 main.dart). I have tried a few methods to retrieve it, but so far I haven't had any luck with trying to grab it. The last attempt I tried was using '$user' (~Line 29), but I still haven't had any luck. I'm still very new to Flutter programming, but I had assumed you could access Shared Preferences data globally as long as you had the Key. So far when I tried using the methods I saw online and in documentation I had no luck transferring the data. Thank you for your help!
main.dart
import 'package:bit/shared_preferences.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:bit/saved_data.dart';
void main() {
runApp(MaterialApp(
title: 'App',
themeMode: ThemeMode.system,
theme: MyThemes.lightTheme,
darkTheme: MyThemes.darkTheme,
home: MyApp(),
));
}
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final theme = MediaQuery.of(context).platformBrightness == Brightness.dark
? 'Dark Theme'
: 'Light Theme';
final user = ''; // Empty String Line 29
var scaffold = Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
// Body Home Page Beginning
body: SingleChildScrollView(
child: Center(
child: Text('Hello $theme!'),
)),
// Body Home Page End
drawer: Drawer(
// Drawer Beginning
child: ListView(
children: [
// Drawer Header
DrawerHeader(
decoration: const BoxDecoration(
color: Colors.blue,
),
child: Stack(
children: const [
Align(
alignment: Alignment.centerLeft,
child: CircleAvatar(
backgroundColor: Colors.white,
radius: 50.0,
)),
Align(
alignment: Alignment.centerRight,
child: Text('$user', // Area To Input Text Line 60
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
),
)),
Align(
alignment: Alignment(1, 0.3),
child: Text(
'Supporter',
style: TextStyle(
color: Colors.white,
fontSize: 15.0,
),
))
],
),
),
// Drawer List
ListTile(
title: const Text('Settings'),
subtitle: const Text('Account Info & Settings'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Settings()),
);
},
trailing: const Icon(Icons.arrow_forward_ios_rounded),
),
],
),
),
// Drawer End
);
return MaterialApp(
title: 'App',
themeMode: ThemeMode.system,
theme: MyThemes.lightTheme,
darkTheme: MyThemes.darkTheme,
home: scaffold,
);
}
}
// Settings Page & Account Information
class Settings extends StatefulWidget {
Settings({Key? key}) : super(key: key);
#override
State<Settings> createState() => _SettingsState();
}
class _SettingsState extends State<Settings> {
final _preferencesService = PreferencesService();
final _usernameController = TextEditingController();
void initState() {
super.initState();
_populateFields();
}
void _populateFields() async {
final settings = await _preferencesService.getSettings();
setState(() {
_usernameController.text = settings.username;
});
}
#override
Widget build(BuildContext context) {
final theme = MediaQuery.of(context).platformBrightness == Brightness.dark
? 'Dark Theme'
: 'Light Theme';
return Scaffold(
appBar: AppBar(
title: const Text(
'Settings'), /* actions: <Widget>[
IconButton(
onPressed: () async {
_saveSettings;
},
icon: const Icon(Icons.save),
tooltip: 'Save Settings')
] */
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 8, 8, 8),
child: Column(
children: [
Column(
// Account
children: [
const Padding(
padding: EdgeInsets.fromLTRB(0, 12, 0, 0),
child: Text('Account Information',
style: TextStyle(
fontSize: 17.0,
))),
Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 12),
child: TextField(
controller: _usernameController,
inputFormatters: [LengthLimitingTextInputFormatter(25)],
decoration: InputDecoration(
hintText: 'Username',
labelText: 'Username',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0)),
),
),
),
],
),
Container(
child: Column(
// App Settings
children: [
// SwitchListTile(value: DarkMode, onChanged: Light => Dark => Light)
// ChangeThemeButtonWidget(),
TextButton(
onPressed: _saveSettings,
child: const Text('Save Settings'),
)
],
),
),
],
),
)));
}
void _saveSettings() {
final newSettings = SettingsModal(
username: _usernameController.text,
);
print(newSettings);
print(_usernameController.text);
_preferencesService.saveSettings(newSettings);
}
}
shared_preferences.dart
import 'package:bit/main.dart';
import 'package:bit/saved_data.dart';
import 'package:shared_preferences/shared_preferences.dart';
class PreferencesService {
Future saveSettings(SettingsModal settings) async {
final preferences = await SharedPreferences.getInstance();
await preferences.setString('username', settings.username);
print('Saved Settings');
}
Future<SettingsModal> getSettings() async {
final preferences = await SharedPreferences.getInstance();
final username = preferences.getString('username')!;
return SettingsModal(
username: username,
);
}
}
saved_data.dart
import 'package:shared_preferences/shared_preferences.dart';
import 'package:bit/main.dart';
class SettingsModal {
final String username;
SettingsModal({
required this.username,
});
}
The issue is coming because, you like to use user which is not a constant. While adding const on Stack's children as Constance, which can be happened in this case, remove const and it won't show any errors.
child: Stack(
children: [
Align(
alignment: Alignment.centerLeft,
child: CircleAvatar(
backgroundColor: Colors.white,
radius: 50.0,
)),
Align(
alignment: Alignment.centerRight,
child: Text(
user,
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
),
)),
],
),
To receive data from future(SharedPreference) we need to wait.
You can use FutureBuilder in this case.
We can provide default value instead of using ! and make it static .
static Future<SettingsModal> getSettings() async {
final preferences = await SharedPreferences.getInstance();
final username = preferences.getString('username') ?? "Not found";
return SettingsModal(
username: username,
);
}
Use PreferencesService.getSettings(), to get data.
Align(
alignment: Alignment.centerRight,
child: FutureBuilder<SettingsModal>(
future: PreferencesService.getSettings(),
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.connectionState ==
ConnectionState.done) {
return Text(
snapshot.data!.username,
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
),
);
}
/// better to handle other cases, included on answer
return CircularProgressIndicator();
},
)),
More about FutureBuilder

How to fix "Too many positional arguments: 1 expected, but 3 found." issue in flutter

I'm new to flutter.
I need to get product information through a form using flutter provider.
I can get one object(like String name value only). But when I add multiple parameters, it shows the following error.
Too many positional arguments: 1 expected, but 3 found.
This is the code I wrote.
Model class
class Item {
String itemName;
String description;
double itemPrice;
Item(this.itemName, this.description, this.itemPrice);
}
ChangeNotifier class
class ItemAddNotifier extends ChangeNotifier {
List<Item> itemList = [];
addItem(String itemName, String description, double itemPrice) {
Item item = Item(itemName, description, itemPrice);
itemList.add(item);
notifyListeners();
}
}
Add items
class AddItems extends StatelessWidget {
final TextEditingController _itemNameTextEditing = TextEditingController();
final TextEditingController _itemDescriptionTextEditing =
TextEditingController();
final TextEditingController _itemPriceTextEditing = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Kavishka'),
),
body: Container(
padding: EdgeInsets.all(30.0),
child: Column(
children: [
TextField(
controller: _itemNameTextEditing,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(15.0),
hintText: 'Item Name',
),
),
SizedBox(
height: 20.0,
),
TextField(
controller: _itemDescriptionTextEditing,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(15.0),
hintText: 'Item Description',
),
),
SizedBox(
height: 20.0,
),
TextField(
controller: _itemPriceTextEditing,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(15.0),
hintText: 'Item Price',
),
),
SizedBox(
height: 20.0,
),
RaisedButton(
child: Text('ADD ITEM'),
onPressed: () async {
if (_itemNameTextEditing.text.isEmpty) {
return;
}
await Provider.of<ItemAddNotifier>(context, listen: false)
.addItem(
_itemNameTextEditing.text,
_itemDescriptionTextEditing.text,
_itemPriceTextEditing.text);
Navigator.pop(context);
},
),
],
),
),
);
}
}
Home Screen
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Kavishka'),
actions: [
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
fullscreenDialog: true,
builder: (context) {
return AddItems();
},
),
);
},
icon: Icon(Icons.add))
],
),
body: Container(
padding: EdgeInsets.all(30.0),
child: Column(
children: [
Consumer<ItemAddNotifier>(builder: (context, itemAddNotifier, _) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: itemAddNotifier.itemList.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.all(15.0),
child: Column(
children: [
Text(
itemAddNotifier.itemList[index].itemName,
style:
TextStyle(fontSize: 20.0, color: Colors.black),
),
],
),
);
});
})
],
),
),
);
}
}
Main
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) {
return ItemAddNotifier();
},
child: MaterialApp(
home: Container(
color: Colors.white,
child: HomeScreen(),
),
),
);
}
}
It shows the error in Item item = Item(itemName, description, itemPrice); line.
If someone can help me to fix this issue.
Thank you.

How to manually add items to listview in Flutter

I have a list of cart items which I am displaying using the code below. Right after this list, I would like to display the total amount. This is how the final result should look like:
Chicken Burger 1X $20.5
Chicken Wrap 1X $9.99
Total $30.49
Container(
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 4),
height: min(widget.order.products.length * 20.0 + 10, 100),
child: ListView(
children: widget.order.products
.map(
(prod) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(child:Text(
prod.title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
)),
Text(
'${prod.quantity}x \$. ${prod.price}',
style: TextStyle(
fontSize: 16,
color: Colors.grey,
),
)
],
),
)
.toList(),
How can I append total to this list?
Here is my suggestion.
I used spread operator to ListView's children for adding Widget related to 'total'.
Additionally I added one item at Container's height because of Total item in ListView.
Below is summary code that I did.
ListView(
children: <Widget> [
...list.map(...).toList(),
TotalWidget(),
]
)
This is full code based your code.
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#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> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: _buildBody(),
floatingActionButton: FloatingActionButton(
onPressed: () {
showModalBottomSheet(
context: context,
backgroundColor: Colors.blueGrey,
isScrollControlled: false,
builder: (context) => Wrap(
children: [
ListView.separated(
shrinkWrap: true,
itemCount: 3,
itemBuilder: (BuildContext context, int index) => ListTile(
title: Text(
'lists[index].listName',
style: TextStyle(
color: Colors.white,
),
),
),
separatorBuilder: (BuildContext context, int index) =>
Divider(),
),
],
),
);
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
Widget _buildBody() {
List<Product> listProduct = [
Product('Chicken Burger', 1, 20.5),
Product('Chicken Wrap', 1, 9.99),
];
double totalAmount = 0;
for (var item in listProduct) {
totalAmount += (item.price * item.quantity);
}
return Container(
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 4),
height: min((listProduct.length + 1) * 20.0 + 10, 100),
child: ListView(
children: [
...listProduct
.map(
(prod) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Text(
prod.title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
)),
Text(
'${prod.quantity}x \$. ${prod.price}',
style: TextStyle(
fontSize: 16,
color: Colors.grey,
),
)
],
),
)
.toList(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Text(
'Total',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
)),
Text(
'$totalAmount',
style: TextStyle(
fontSize: 16,
color: Colors.grey,
),
)
])
],
),
);
}
}
class Product {
String title;
int quantity;
double price;
Product(this.title, this.quantity, this.price);
}
Edit 1, after op updated more info in comments:
Column(children: [ Text(widget.order.totalPrice.toString()),
Flexible(child:
ListView(
children:
widget.order.products
.map((prod) => Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(child:Text(
prod.title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
)),
Text(
'${prod.quantity}x \$. ${prod.price}',
style: TextStyle(
fontSize: 16,
color: Colors.grey,
),
)
],
),
)
.toList())]),
Since total isn't being stored as a single variable your list\cart object. You need to create a double totalPrice = 0.0;
then use a forLoop to add the values
for (var prod in widget.order.products) {
totalPrice += (prod.price * prod.quantity);}
Display this totalPrice wherever you want, you can't have it in the listView though.
If you want add items to ListView, first you have to add those items to your List (for example order.products or new one) and then use state management approach to re render ListView. if your logic is simple you can use stateful widget.
example code:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: MyList(),
),
);
}
}
class MyList extends StatefulWidget {
#override
_MyListState createState() => _MyListState();
}
class _MyListState extends State<MyList> {
List<String> orders = ["order1", "order2", "order3"];
#override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: ListView(
children: orders
.map(
(String e) => Card(
child: ListTile(
title: Text(e),
),
),
)
.toList(),
),
),
TextButton(
onPressed: () {
List<String> extraFields = ["field1", "field2"];
setState(
() {
orders.addAll(extraFields);
},
);
},
child: Text("Add extra fields"),
),
],
);
}
}
For that, you'd have to learn state management. To make things simple we'll use the built-in StreamBuilder to provide the data. Its best practice to separate your ui from your business logic so I'll do it here.
In order to use a StreamBuilder, you'd have to provide it a Stream<T> where T is your variable's type. In your case, its a List<String>. Lets write it in another file that holds all your buisness logic.
product_bloc.dart:
class ProductBloc {
final List<String> _productList = ["Item One", "Item Two"];
StreamController<List<String>> _products = StreamController<List<String>>();
Stream<List<String>> get products => _products.stream;
ProductBloc() {
_products.add(_productList);
}
void addProductAfterDelay() async {
_productList.add("Item Three");
await Future.delayed(const Duration(seconds: 3));
_products.add(_productList);
}
}
product_screen.dart:
StreamBuilder<List<String>>(
initialData: [],
builder: (context, snapshot) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Text(snapshot.data[index]);
});
},
);

Flutter : Navigate with persistent side bar

I'm creating a flutter web project that looks like a dashboard. It has a side navigation and a body area that displays different screens.
To achieve this, I have divided my screen into two parts using Expanded and given them a flex value.
And to display different screens I have used IndexedStack.
Here is my main.dart file :
import 'package:flutter/material.dart';
import 'package:xxx/screens/courses/courses_screen.dart';
import 'package:xxx/side_navigation/menu_item.dart';
import 'package:xxx/componnents/header.dart';
import 'package:websafe_svg/websafe_svg.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
scaffoldBackgroundColor: Color(0xfffafafa),
fontFamily: 'Poppins',
),
home: MainScreenLayout(),
);
}
}
class MainScreenLayout extends StatefulWidget {
#override
_MainScreenLayoutState createState() => _MainScreenLayoutState();
}
class _MainScreenLayoutState extends State<MainScreenLayout> {
int selectedIndex = 0;
int hoverIndex = -1;
Color bgColor = Colors.transparent;
final List<MenuItem> menus = <MenuItem>[
MenuItem(label: 'Home', icon: Icons.home, screen: Container()),
MenuItem(label: 'Courses', icon: Icons.add, screen: Container()),
MenuItem(label: 'Students', icon: Icons.face_outlined, screen: Container()),
MenuItem(label: 'Home', icon: Icons.home, screen: Container()),
MenuItem(label: 'Courses', icon: Icons.add, screen: Container()),
MenuItem(label: 'Students', icon: Icons.face_outlined, screen: Container()),
];
final List<Widget> _screens = [
Container(
child: Image.asset(
'assets/try.png',
width: 100,
height: 100,
),
),
CoursesScreen(),
Container(
child: WebsafeSvg.asset('assets/folder_icon.svg'),
),
];
void _changeBg(int index) {
setState(() {
hoverIndex = index;
});
}
void _resetBg() {
setState(() {
hoverIndex = -1;
});
}
#override
Widget build(BuildContext context) {
double _width = MediaQuery.of(context).size.width;
return Scaffold(
body: Row(
children: [
Expanded(
flex: 2,
child: Container(
decoration: BoxDecoration(
color: Color(0xffffffff),
borderRadius: BorderRadius.only(
topRight: Radius.circular(_width * 0.03),
bottomRight: Radius.circular(_width * 0.03),
),
),
child: Column(
children: <Widget>[
SizedBox(
height: 50,
),
Flexible(
child: ListView.builder(
itemCount: menus.length,
itemBuilder: (BuildContext context, int index) {
return MouseRegion(
onHover: (event) {
_changeBg(index);
},
onExit: (event) {
_resetBg();
},
child: MenuItemLayout(
bgColor: hoverIndex == index
? Color(0xfffafafa)
: Colors.transparent,
menuItem: menus[index],
isSelected: selectedIndex == index ? true : false,
onTap: () {
setState(() {
selectedIndex = index;
});
},
),
);
},
),
),
],
),
),
),
Expanded(
flex: 7,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Header(
label: menus[selectedIndex].label,
),
Expanded(
child: Container(
child: IndexedStack(
index: selectedIndex,
children: _screens,
),
),
),
],
),
)
],
),
);
}
}
Here is the code for one of the screens (CourseScreen)
import 'package:flutter/material.dart';
import 'package:vjti_dashboard/componnents/responsive.dart';
import 'package:vjti_dashboard/screens/courses/courses_card.dart';
import 'package:vjti_dashboard/screens/courses/courses_model.dart';
class CoursesScreen extends StatelessWidget {
final List<CoursesModel> allCourses = [
CoursesModel(courseId: '123', courseName: 'Ecommerce'),
CoursesModel(courseId: '123', courseName: 'Big Data Analytics'),
CoursesModel(courseId: '123', courseName: 'User Experience Design'),
CoursesModel(courseId: '123', courseName: 'Technical Seminar'),
CoursesModel(courseId: '123', courseName: 'Elective 1'),
];
#override
Widget build(BuildContext context) {
return ListView(
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: [
Sample(
headingLabel: 'First Year',
allCourses: allCourses,
courseColor: Color(0xffEEF1E6),
),
Sample(
headingLabel: 'Second Year',
allCourses: allCourses,
courseColor: Color(0xffF9F1D6),
),
Sample(
headingLabel: 'Third Year',
allCourses: allCourses,
courseColor: Color(0xffE2F0CB),
),
],
);
}
}
class Sample extends StatelessWidget {
final String headingLabel;
final Color courseColor;
final List<CoursesModel> allCourses;
const Sample({Key key, this.allCourses, this.headingLabel, this.courseColor})
: super(key: key);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
GestureDetector(
onTap: () {},
child: Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Color(0xffd6d6d6),
width: 1.0,
),
),
),
child: Text(headingLabel),
),
),
GridView.count(
primary: false,
physics: ScrollPhysics(), // to disable GridView's scrolling
shrinkWrap: true,
padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 0),
crossAxisSpacing: 20,
childAspectRatio: 3 / 1,
mainAxisSpacing: 20,
crossAxisCount: Responsive.isDesktop(context)
? 4
: Responsive.isTablet(context)
? 3
: 2,
children: List.generate(
allCourses.length,
(index) {
return CourseCard(
coursesModel: allCourses[index],
containerColor: courseColor,
);
},
),
),
],
),
);
}
}
The side navigation works as it should and different pages are loaded while the navigation sticks to the left.
When I click on any widget on the CourseScreen, I want to open another screen that would replace CourseScreen but the navigation should still be there.
How can I achieve this?
Note : I'm new to flutter and most of the code that I have written is not perfect and probably is not a good way. I would appreciate if you can point out bad codes in the above files.
Thank You!!!
onGenerateRoute: (settings) => MaterialPageRoute(
builder: (context) => Parent(),