How to handle navigation in the flutter package? - flutter

I have created a flutter package (say weight_calculator) and using it from the parent flutter app.
Now I want to navigate from screen A to screen B in weight_calculator package, but I am not able to use the Named routes as this package doesn't have a Material App.
What will be the correct approach to do that?
Currently, I am creating a new MaterialApp inside the package.

This actually works if you use go_route.
Here is an example where u have a app showing a screen from a package, and that package screen is navigating to a screen from the main app.
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:home_page/home_page.dart';
void main() => runApp(const MyApp());
/// The route configuration.
final GoRouter _router = GoRouter(
routes: <RouteBase>[
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) {
return const HomeScreen();
},
routes: <RouteBase>[
GoRoute(
path: 'details',
builder: (BuildContext context, GoRouterState state) {
return const DetailsScreen();
},
),
],
),
],
);
/// The main app.
class MyApp extends StatelessWidget {
/// Constructs a [MyApp]
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: _router,
);
}
}
/// The details screen
class DetailsScreen extends StatelessWidget {
/// Constructs a [DetailsScreen]
const DetailsScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Details Screen')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <ElevatedButton>[
ElevatedButton(
onPressed: () => context.go('/'),
child: const Text('Go back to the Home screen'),
),
],
),
),
);
}
}
and you also just need to make a local package with the HomeScreen widget.
library home_page;
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
/// The home screen
class HomeScreen extends StatelessWidget {
/// Constructs a [HomeScreen]
const HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home Screen')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/details'),
child: const Text('Go to the Details screen'),
),
const Text("hello"),
],
),
),
);
}
}
Worked out of the box for me.
Also this can be improved by abstraction the routes, and injecting them in to the package from the main app.

Related

alert dialog pops up even when different route is called

I have a simple flutter app with two screens. On the first screen, i have an alert dialog that pops up every time a user visits the screen.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() => runApp(const MyApp());
/// The route configuration.
final GoRouter _router = GoRouter(
routes: <RouteBase>[
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) {
return const HomeScreen();
},
routes: <RouteBase>[
GoRoute(
path: 'details',
builder: (BuildContext context, GoRouterState state) {
return const DetailsScreen();
},
),
],
),
],
);
/// The main app.
class MyApp extends StatelessWidget {
/// Constructs a [MyApp]
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp.router(
routeInformationProvider: _router.routeInformationProvider,
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate);
}
}
/// The home screen
class HomeScreen extends StatefulWidget {
/// Constructs a [HomeScreen]
const HomeScreen({Key? key}) : super(key: key);
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
void initState() {
Timer(const Duration(seconds : 1), (() {
showDialog(
context: context,
builder: (context) {
return someDialogy();
});
print('i have been called forth');
}));
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home Screen')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/details'),
child: const Text('Go to the Details screen'),
),
],
),
),
);
}
}
/// The details screen
class DetailsScreen extends StatelessWidget {
/// Constructs a [DetailsScreen]
const DetailsScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Details Screen')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <ElevatedButton>[
ElevatedButton(
onPressed: () => context.go('/'),
child: const Text('Go back to the Home screen'),
),
],
),
),
);
}
}
Widget someDialogy () {
return AlertDialog(
content: Center(
child: Text('data'),
),
);
}
When i try to navigate to my second screen using a hyperlink on web say http://localhost/secondscreen, the popup from my first screen shows up.
My guess is that in constructing the route stack, flutter calls the initstate in my first page which does show my popup. What is the best way to go around this while maintaining the popup that shows when my first page is called?
it would be helpful to show some mode code on this, like the somedialogy() method
anyways I suspect the issue might be with your routing solution,
try
onPressed: () {
// Navigate to the second route when the button is pressed
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
},
I think its all about Timer
try this:
Future.delayed(Duration(seconds: 1), () {
showDialog(
context: context,
builder: (context) {
return someDialogy();
});});
read this for more about Timer Understanding Flutter’s Timer class and Timer.periodic
and this 2 Types of Flutter Delay Widgets

Flutter screen flickers with flutter 3.3.2

Even with a simple app, when I switch from one page to another, the screen flickers. Initially, I was testing this on flutter 3.0.2 and then moved to 3.3.2. On both versions I observe the flickering. Searching on google I see that others have observed this with ad_mob, but in my case I don't have ad_mob.
Here is main.dart
import 'package:flutter/material.dart';
import 'package:navigate3/page_1.dart';
import 'package:navigate3/page_2.dart';
import 'package:navigate3/page_3.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Main app',
initialRoute: '/Page1',
routes: {
'/Page1': (context) => Page_1(),
'/Page2': (context) => Page_2(),
'/Page3': (context) => Page_3(),
},
);
}
}
And here is page_1.dart. page_2.dart and page_3.dart are similar to page_1.dart
import 'package:flutter/material.dart';
class Page_1 extends StatelessWidget {
const Page_1({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text('Page 1'),
),
body: Center(
child: Column(
children: [
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/Page2');
},
child: const Text("Goto page 2"))
],
),
),
));
}
}

go_router - Is there a way to push twice the same page?

I'm using go_router to build an application that
does "dummy" pushes of routes (a bit like Twitter). It is possible to start with a page /a ,push /b, then /c, ... and then push again /a.
When a page is pushed twice, an assert fails: assert(!keyReservation.contains(key)); which ensures a key is used only once in the Navigator.
Is there a way to be able to push twice the same page with go_router?
Here is a small code snippet:
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() {
runApp(const MyApp());
}
final router = GoRouter(
initialLocation: '/a',
routes: [
GoRoute(
path: '/a',
builder: (_, __) => const MyWidget(path: '/a'),
),
GoRoute(
path: '/b',
builder: (_, __) => const MyWidget(path: '/b'),
),
],
);
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp.router(
routeInformationProvider: router.routeInformationProvider,
routeInformationParser: router.routeInformationParser,
routerDelegate: router.routerDelegate,
);
}
}
class MyWidget extends StatelessWidget {
const MyWidget({
required this.path,
Key? key,
}) : super(key: key);
final String path;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(path),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
onPressed: () {
context.push('/a');
},
child: const Text('/a'),
),
TextButton(
onPressed: () {
context.push('/b');
},
child: const Text('/b'),
),
],
)),
);
}
}
The issue can be triggered by clicking on the TextButton with "/a".
It was a bug in go_router (see this issue) which has been solved in the version 4.2.3.
So now you should be able to push the same page twice.

BlocProvider Error: Could not find the correct Provider<CounterBloc>

I wrote basic BlocProvider where there is no navigation, but still, I am getting below error
Error: Could not find the correct Provider above this
CounterPage Widget
Here is the link to the reproducible project on GitHub.
In short, you can just wrap the child in Builder as follows or create a new StatelessWidget or StatefulWidget for the child.
Providers injects the objects to the child element. It turns out the BuildContext object from build() is before the CounterBloc is injected.
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:japa_counter/counter_widget/counter_widget.dart';
import 'counter_widget/model/counter_model.dart';
import 'counter_widget/bloc/counter_bloc.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Japa Counter',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const CounterPage(),
);
}
}
class CounterPage extends StatelessWidget {
const CounterPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocProvider<CounterBloc>(
create: (_) => CounterBloc(),
// -------------------- Wrapped in Builder ----------------------
child: Builder(
builder: (context) => Scaffold(
appBar: AppBar(
title: const Text("Japa Counter"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FloatingActionButton(
onPressed: () =>
context.read<CounterBloc>().add(IncrementCounter()),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
const SizedBox(width: 60.0, child: Divider()),
FloatingActionButton(
onPressed: () =>
context.read<CounterBloc>().add(DecrementCounter()),
tooltip: 'Decrement',
child: const Icon(Icons.remove),
),
const CounterWidget(),
],
),
),
)
),
// This trailing comma makes auto-formatting nicer for build methods.
);
}
}

How can i make full menu come out while mouse hover in the AppBar in flutter?

This is what I am trying to achieve:
I've thought about onHover in MouseRegion widget and trying to see if this code works in body part.
I was going to implement this in AppBar in scaffold after i see this works in body part but i couldn't.
Does anyone know correct way?
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: Center(
child: MyStatefulWidget(),
),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
showMenus(BuildContext context) async {
await showMenu(
context: context,
position: RelativeRect.fromLTRB(100, 100, 100, 100),
items: [
PopupMenuItem(
child: Text("View"),
),
PopupMenuItem(
child: Text("Edit"),
),
PopupMenuItem(
child: Text("Delete"),
),
],
);
}
#override
Widget build(BuildContext context) {
return ConstrainedBox(
constraints: BoxConstraints.tight(Size(300.0, 200.0)),
child: MouseRegion(
onHover: showMenus(context),
child: Container(
color: Colors.lightBlueAccent,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have entered or exited this box this many times:'),
],
),
),
),
);
}
}
Your only problem is incorrectly invoking the method.
On your MouseRegion onHover it should have a () => before invoking the method:
// the onHover event gives you an event object
onHover: (event) => showMenus(context),