Presume I have three classes: main, EndpointList and FillDataClass.
I have defined some routes in my main class as such:
void main() {
runApp(MaterialApp(
title: 'Named Routes Demo',
initialRoute: '/',
routes: {
'/': (context) => MyApp(),
'/endpoint_list': (context) => EndpointList(),
},
));
}
My EndpointList class is a simple list view:
import 'package:flutter/material.dart';
class EndpointData {
EndpointData(this.name, this.id, this.token, this.isIncoming);
final String name;
final String id;
final String token;
bool isIncoming;
}
class EndpointList extends StatefulWidget {
EndpointList({Key key}) : super(key: key);
#override
_EndpointList createState() => new _EndpointList();
}
class _EndpointList extends State<EndpointList> {
List<EndpointData> endpointList = <EndpointData>[];
#override
Widget build(BuildContext context) {
// build and show list
}
void insertEndpoint(EndpointData endpointData){
endpointList.add(endpointData);
}
}
My question is, how can I access and instance of EndpointList, from class that is not main, in order to call the insertEndpoint method?
In my java mind, I want to do this:
Endpoint endpoint = new Endpoint(); // This is done in route in main class
And then from class FillDataClass (presuming endpoint has been properly instanced in FillDataClass via constructor):
endpoint.insertEndpoint(data);
How can I create and access endpoint in order to populate, and then display, my list?
Use a separate the Endpoint class which will contain an insertEndPoint mothed.
class EndpointData {
EndpointData(this.name, this.id, this.token, this.isIncoming);
final String name;
final String id;
final String token;
bool isIncoming;
List<EndpointData> _endpointList = <EndpointData>[];
void insertEndpoint(EndpointData endpointData){
_endpointList.add(endpointData);
}
}
Then in your UI
class EndpointListUI extends StatefulWidget {
EndpointListUI({Key key}) : super(key: key);
#override
_EndpointListUI createState() => new _EndpointListUI();
}
class _EndpointListUI extends State<EndpointListUI> {
//You can create instance from anywhere and insert data to it
List<EndpointData> endpointList = <EndpointData>[];
endpointList.add(endpointData);
#override
Widget build(BuildContext context) {
// build and show list
}
}
Related
I'm trying to use a url where I get a parameter and assign that parameter to a variable inside the web file.
for example my domain is example.com and in this website i need an id for user. I want to make example.com/?id=123 and getting that 123 id and giving a variable 123 value
In flutter web device It works but when i host this files it doesnt work on real domain. And flutter giving me a
Could not navigate to initial route.
The requested route name was: "/?id=sezen#gmail.com"
There was no corresponding route in the app, and therefore the initial route specified will be
ignored and "/" will be used instead.
Here is my code
void main() async {
WidgetsFlutterBinding.ensureInitialized();
setPathUrlStrategy();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
String myurl = Uri.base.toString(); //get complete url
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// This widget is the root of your application.
#override
void initState() {
super.initState();
getParams();
}
#override
Widget build(BuildContext context) {
getParams();
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'QR Numaratör',
home: MyHomePage(id: mail),
);
}
}
void getParams() {
var uri = Uri.dataFromString(window.location.href);
Map<String, String> params = uri.queryParameters;
var origin = params['id'];
mail = origin;
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.id}) : super(key: key);
String? id = Uri.base.queryParameters["id"];
#override
State<MyHomePage> createState() => _MyHomePageState();
}
In your MaterialApp widget, you need to specify onGenerateRoute parameter.
Something like this:
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'QR Numaratör',
home: MyHomePage(id: mail),
onGenerateRoute: (settings) {
switch (kIsWeb? Uri.parse(settings.name).path : settings.name) {
case'/':
Map args = settings.arguments as Map;
if(kIsWeb && args == null) {
args=Uri.parse(settings.name).queryParameters;
}
return MaterialPageRoute(builder: (_) => MyHomePage(id: args[id]));
}
});
I'm trying to use a url where I get a parameter and assign that parameter to a variable inside the web file.
for example my domain is example.com and in this website i need an id for user. I want to make example.com/?id=123 and getting that 123 id and giving a variable 123 value
In flutter web device It works but when i host this files it doesnt work on real domain. And flutter giving me a
Could not navigate to initial route.
The requested route name was: "/?id=sezen#gmail.com"
There was no corresponding route in the app, and therefore the initial route specified will be
ignored and "/" will be used instead.
Here is my code
void main() async {
WidgetsFlutterBinding.ensureInitialized();
setPathUrlStrategy();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
String myurl = Uri.base.toString(); //get complete url
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// This widget is the root of your application.
#override
void initState() {
super.initState();
getParams();
}
#override
Widget build(BuildContext context) {
getParams();
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'QR Numaratör',
home: MyHomePage(id: mail),
);
}
}
void getParams() {
var uri = Uri.dataFromString(window.location.href);
Map<String, String> params = uri.queryParameters;
var origin = params['id'];
mail = origin;
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.id}) : super(key: key);
String? id = Uri.base.queryParameters["id"];
#override
State<MyHomePage> createState() => _MyHomePageState();
}
In your MaterialApp widget, you need to specify onGenerateRoute parameter.
Something like this:
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'QR Numaratör',
home: MyHomePage(id: mail),
onGenerateRoute: (settings) {
switch (kIsWeb? Uri.parse(settings.name).path : settings.name) {
case'/':
Map args = settings.arguments as Map;
if(kIsWeb && args == null) {
args=Uri.parse(settings.name).queryParameters;
}
return MaterialPageRoute(builder: (_) => MyHomePage(id: args[id]));
}
});
I wanted to implements a simple tutorial of how parsing an Rss Feed with Flutter, here is my code:
import 'package:flutter/material.dart';
import 'package:webfeed/webfeed.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
import 'package:cached_network_image/cached_network_image.dart';
class RSSParser extends StatefulWidget {
#override
_RSSParserState createState() => _RSSParserState();
}
class _RSSParserState extends State<RSSParser> {
final String url = "https://www.90min.com/posts.rss";
RssFeed _feed;
String _title;
static const String loadingFeedMsg = 'Loading Feed...';
static const String feedLoadErrorMsg = 'Error Loading Feed.';
static const String feedOpenErrorMsg = 'Error Opening Feed.';
Future<RssFeed> loadFeed() async{
try{
final client = http.Client();
final response = await client.get(url);
return RssFeed.parse(response.body);
}
catch(e){
}
return null;
}
updateTitle(title){
setState(() {
_title = title;
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
updateTitle(widget.title);
}
updateFeed(feed){
setState(() {
_feed = feed;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_title),
),
);
}
}
The problem is that i got a compilation error in that instruction
updateTitle(widget.title);
with the following error message:
The getter 'title' isn't defined for the type 'RSSParser'
In the tutorial, it works fine!!
Do you have an idea how to solve this?
Thank you
You haven't declared title for you RSS widget. It should look something like ths:
class RSSParser extends StatefulWidget {
final String title;
const RSSParser({required this.title});
This should solve your error.
This is not working because there is not title in RSS class.
I think you are not clear with use of widget.something. It means that in the class which extends StatefulWidget there is a something parameter which i need to get in stateObject.
See the code to understand.
class YellowBird extends StatefulWidget {
const YellowBird({ Key? key }) : super(key: key);
String someData = 'SomeData'; // Some data
#override
_YellowBirdState createState() => _YellowBirdState();
}
//This is the state object
class _YellowBirdState extends State<YellowBird> {
// Now that if you need some data from the above class. You use use this widget.someData to get it here
String getHere = widget.someData ;
#override
Widget build(BuildContext context) {
return Container(color: const Color(0xFFFFE306));
}
}
I am still with my first bloc based app, adding features. While previously, I stored some of my page specific data with the bloc class, for the last feature, I now moved most variables into its repository. I already feared that the instance of calling the repository gets lost, afterwards, which now proved true.
Is there a proper, easy way to make the instance persistent?
I know of inherited widgets, however, I have not yet figured out how to implement this and my question around this unfortunately remained unanswered. It would be great, if someone could point me to some direction!
In general, my idea was to have the api dealing with local files and online data, the repository with frequently re-used data (session data, presented data etc) and helper variables within the bloc. So when the UI requests data, the bloc asks the repository which will either return a value stored in a variable or request a value from the api.
This is, how the strucuture basically looks like (hope I have not missed anything significant)
void main() async {
final UserRepository userRepository = UserRepository(); // <===== userRepository initialized
runApp(MyApp(userRepository: UserRepository()));
}
class MyApp extends StatelessWidget {
MyApp({Key key, this.userRepository}) : assert(userRepository != null), super(key: key);
final UserRepository userRepository;
#override
Widget build(BuildContext context) {
return BlocProvider<UserBloc>( <====== userBloc injection to top of widget tree
create: (_) => UserBloc(userRepository: userRepository)..add(AppStarted()),
child: App(),
);
}
}
// =================================================== APP WITH ROUTES
class App extends StatelessWidget {
App({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return CupertinoApp(
routes: {
'/': (_) => HomePage(),
'feature 1': (_) => HomePage(),
},
);
}
}
// =================================================== LANDING PAGE WITH MAIN MENU
class HomePage extends StatefulWidget {
HomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('MathUup'),
),
child: SafeArea(
child: CupertinoButton(
child: Text('Feature 1',
onPressed: () => Navigator.pushNamed(context, 'feature 1'),
),)));
}}
// =================================================== BLOC
class UserBloc extends Bloc<UserEvent, UserState> {
UserBloc({this.userRepository}) : super(AppInitial());
final UserRepository userRepository;
...
final user = await userRepository.getActiveUserData(userId);
final lastSessionData = await userRepository.getLastSession(userId);
...
}
// =================================================== REPOSITORY
class UserRepository {
UserRepository();
final UserApiClient achievementsApiClient = UserApiClient();
final SessionsApiClient sessionsApiClient = SessionsApiClient();
UserSession activeUserSession;
User activeUserData;
Future<String> getLastUserId() async {
final lastUserId = await sessionsApiClient.getLastUserId();
return lastUserId;
}
Future<UserSession> getActiveUser() async {
if (activeUserSession == null) {
activeUserSession = await sessionsApiClient.getLastUser();
}
return activeUserSession;
}
}
This line is creating and initializing your user repository:
final UserRepository userRepository = UserRepository(); // <===== userRepository initialized
However, this line is not passing that repository, it's creating a new repository, ignoring the one you just initialized:
runApp(MyApp(userRepository: UserRepository()));
I think you meant to use the variable you already have:
runApp(MyApp(userRepository: userRepository));
I have 2 screens in my Flutter app: a list of records and a screen for creating and editing records.
If I pass an object to the second screen that means I am going to edit this and if I pass null it means that I am creating a new item. The editing screen is a Stateful widget and I am not sure how to use this approach https://flutter.io/cookbook/navigation/passing-data/ for my case.
class RecordPage extends StatefulWidget {
final Record recordObject;
RecordPage({Key key, #required this.recordObject}) : super(key: key);
#override
_RecordPageState createState() => new _RecordPageState();
}
class _RecordPageState extends State<RecordPage> {
#override
Widget build(BuildContext context) {
//.....
}
}
How can I access recordObject inside _RecordPageState?
To use recordObject in _RecordPageState, you have to just write widget.objectname like below
class _RecordPageState extends State<RecordPage> {
#override
Widget build(BuildContext context) {
.....
widget.recordObject
.....
}
}
Full Example
You don't need to pass parameters to State using it's constructor.
You can easily access these using widget.myField.
class MyRecord extends StatefulWidget {
final String recordName;
const MyRecord(this.recordName);
#override
MyRecordState createState() => MyRecordState();
}
class MyRecordState extends State<MyRecord> {
#override
Widget build(BuildContext context) {
return Text(widget.recordName); // Here you direct access using widget
}
}
Pass your data when you Navigate screen :
Navigator.of(context).push(MaterialPageRoute(builder: (context) => MyRecord("WonderWorld")));
class RecordPage extends StatefulWidget {
final Record recordObject;
RecordPage({Key key, #required this.recordObject}) : super(key: key);
#override
_RecordPageState createState() => new _RecordPageState(recordObject);
}
class _RecordPageState extends State<RecordPage> {
Record recordObject
_RecordPageState(this. recordObject); //constructor
#override
Widget build(BuildContext context) {. //closure has access
//.....
}
}
example as below:
class nhaphangle extends StatefulWidget {
final String username;
final List<String> dshangle;// = ["1","2"];
const nhaphangle({ Key key, #required this.username,#required this.dshangle }) : super(key: key);
#override
_nhaphangleState createState() => _nhaphangleState();
}
class _nhaphangleState extends State<nhaphangle> {
TextEditingController mspController = TextEditingController();
TextEditingController soluongController = TextEditingController();
final scrollDirection = Axis.vertical;
DateTime Ngaysx = DateTime.now();
ScrollController _scrollController = new ScrollController();
ApiService _apiService;
List<String> titles = [];
#override
void initState() {
super.initState();
_apiService = ApiService();
titles = widget.dshangle; //here var is call and set to
}
I have to Navigate back to any one of the screens in the list pages but when I did that my onTap function stops working and navigation stops.
class MyBar extends StatefulWidget {
MyBar({this.pageNumber});
final pageNumber;
static const String id = 'mybar_screen';
#override
_MyBarState createState() => _MyBarState();
}
class _MyBarState extends State<MyBar> {
final List pages = [
NotificationScreen(),
AppointmentScreen(),
RequestBloodScreen(),
ProfileScreen(),
];
#override
Widget build(BuildContext context) {
var _selectedItemIndex = widget.pageNumber;
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
elevation: 0,
backgroundColor: Colors.white,
unselectedItemColor: Colors.grey.shade700,
selectedItemColor: Color(kAppColor),
selectedIconTheme: IconThemeData(color: Color(kAppColor)),
currentIndex: _selectedItemIndex,
type: BottomNavigationBarType.fixed,
onTap: (int index) {
setState(() {
_selectedItemIndex = index;
});
},
You should use a Pub/Sub mechanism.
I prefer to use Rx in many situations and languages. For Dart/Flutter this is the package: https://pub.dev/packages/rxdart
For example, you can use a BehaviorSubject to emit data from widget A, pass the stream to widget B which listens for changes and applies them inside the setState.
Widget A:
// initialize subject and put it into the Widget B
BehaviorSubject<LiveOutput> subject = BehaviorSubject();
late WidgetB widgetB = WidgetB(deviceOutput: subject);
// when you have to emit new data
subject.add(deviceOutput);
Widget B:
// add stream at class level
class WidgetB extends StatefulWidget {
final ValueStream<LiveOutput> deviceOutput;
const WidgetB({Key? key, required this.deviceOutput}) : super(key: key);
#override
State<WidgetB> createState() => _WidgetBState();
}
// listen for changes
#override
void initState() {
super.initState();
widget.deviceOutput.listen((event) {
print("new live output");
setState(() {
// do whatever you want
});
});
}
In my app, often instead of using stateful widgets, I use mainly ChangeNotifierProvider<T> in main.dart, some model class
class FooModel extends ChangeNotifier {
var _foo = false;
void changeFooState() {
_foo = true;
notifyListeners();
}
bool getFoo () => _foo;
}
and
var foo = context.read<FooModel>();
# or
var foo = context.watch<FooModel>();
in my stateless widgets. IMO this gives me more precise control over the rebuilding upon runtime state change, compared to stateful widgets.
The recipe can be found in the official docs, the concept is called "lifting state up".