my appbar is disappeared when i go new page using navigator
actually, there was a yellow underline on the new page's text.
I found that's reason is the page didn't have Material.
so i added the Material Widget on that, so I could fixed the problem of text underline.
i think appbar disappeared problem also relates with Material,
but even i wrapped the code with Material widget, still the problem is not solved.
what can i do?
here is the navigator code.
exhibition[i] is what i wanted to transmit to new page(=DetailScreen)
InkWell(
onTap: () {Navigator.push(
context,
MaterialPageRoute(builder: (context)=>DetailScreen(exhibition: exhibitions[i])),
);
And the below is DetailScreen(new page)'s code.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'model_exhibitions.dart';
class DetailScreen extends StatefulWidget {
final Exhibition exhibition;
DetailScreen({required this.exhibition});
State<DetailScreen> createState() => _DetailScreenState();
}
class _DetailScreenState extends State<DetailScreen> {
bool bookmark = false;
#override
void initState() {
super.initState();
bookmark = widget.exhibition.bookmark;
}
#override
Widget build(BuildContext context) {
return Material(
child: SingleChildScrollView(
padding: EdgeInsets.fromLTRB(20, 40, 0, 0),
child: Row( ...
Thank you for your help sincerely
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'model_exhibitions.dart';
class DetailScreen extends StatefulWidget {
final Exhibition exhibition;
DetailScreen({required this.exhibition});
State<DetailScreen> createState() => _DetailScreenState();
}
class _DetailScreenState extends State<DetailScreen> {
bool bookmark = false;
#override
void initState() {
super.initState();
bookmark = widget.exhibition.bookmark;
}
#override
Widget build(BuildContext context) {
return Material(
child: Scaffold( //add scaffold
body: SingleChildScrollView(
padding: EdgeInsets.fromLTRB(20, 40, 0, 0),
child: Row( ...
It will not work without scaffold.
I think you are not getting what scaffold is and how it's behaviour
is?
Scaffold is a widget which provides your screen/route a default
behaviour similar to your android/ios screens like AppBar, Body,
Title, FloatingActionButton, Drawer etc.
So that you do not have to make yourself a new structure.
If you are not using scaffold, then your page will act like a plain
body structure in which you have to fill custom widgets as per your
requirements.
Related
I am creating a flutter Web application, but have issues when resizing the window with a SingleChildScrollView + ScrollController.
When I resize the browser window, the page "snaps" back up to the very top. Being a web app, most of the page "sections" are made from Columns with responsively coded widgets as children, with such widgets as Flexible or Expanded. From what I have read, the SingleChildScrollView widget doesn't work well with Flexible or Expanded widgets, so I thought that may be my issue.
For testing purposes, I created a new page with a single SizedBox that had a height of 3000, which would allow me to scroll. After scrolling to the bottom and resizing the window, I was still snapped up to the top of the page. Thus, with or without using Expanded or Flexible widgets, I have the same result.
Test with a SizedBox only
#override
Widget build(BuildContext context) {
return Scaffold(
color: Colors.white,
body: SingleChildScrollView(
controller: controller.scrollController,
primary: false,
child: Column(
children: [
SizedBox(
width: 150,
height: 3000,
),
],
),
),
);
}
I am using Getx with this project to try getting a demo app up and running a bit quicker while I am still learning the core concepts. Below is my controller.
Controller
class HomePageScrollControllerX extends GetxController {
late ScrollController scrollController;
#override
void onInit() {
super.onInit();
scrollController = ScrollController(
initialScrollOffset: 0.0,
keepScrollOffset: true,
);
}
}
Thank you in advance for any insight on this subject!
EDIT
I have added a listener on my ScrollController, which is able to print to the console that I am scrolling. However, the listener does not get called when I resize the window (tested in both Chrome and Edge).
Currently, I believe my only option is to use the listener to update an "offset" variable in the controller, and pass the window's width over to the controller when the widget rebuilds. If done properly, I should be able to use the controller to scroll to the saved offset. Something like this:
if (scrollController.hasClients) {
if (offset > scrollController.position.maxScrollExtent) {
scrollController.jumpTo(scrollController.position.maxScrollExtent);
} else if (offset < scrollController.position.minScrollExtent) {
scrollController.jumpTo(scrollController.position.minScrollExtent);
} else {
scrollController.jumpTo(offset);
}
}
However, I feel like this shouldn't be necessary - and I bet this solution would be visually evident to the user.
Edit 2
While I did get this to work with adding the below code just before the return statement, it appears that my initial thoughts were correct. When I grab the edge of the window and move it, it pops up to the top of the window, then will jump to the correct scroll position. It looks absolutely terrible!
#override
Widget build(BuildContext context) {
Future.delayed(Duration.zero, () {
controller.setWindowWithAndScroll(MediaQuery.of(context).size.width);
});
return PreferredScaffold(
color: Colors.white,
body: SingleChildScrollView(
controller: controller.scrollController,
......
I implemented your code without getX by initializing the scrollController as a final variable outside your controller class. I ran it on microsoft edge and did not face the issue you are describing. What's causing your problem is most probably the way you are handling your state management with getX. I'm guessing your onInit function is run multiple times when you are resizing your web page and that's why the page snaps back up. I would recommend logging how many times the onInit function is called.
I found the answer, and it was my scaffold causing the issue - specifically, the scaffold key. But, before that, the Getx usage to get the answer is very easy, so for those of you looking for that particular answer, it is shown below.
Getx Controller
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
class HomePageScrollControllerX extends GetxController {
late ScrollController scrollController;
#override
void onInit() {
super.onInit();
scrollController =
ScrollController(keepScrollOffset: true, initialScrollOffset: 0.0);
}
#override
void onClose() {
super.onClose();
scrollController.dispose();
}
}
Stateless Widget Build Function
class HomePage extends StatelessWidget {
HomePage({
Key? key,
}) : super(key: key);
// All child widgets can use Get.find(); to get instance
final HomePageScrollControllerX controller =
Get.put(HomePageScrollControllerX());
#override
Widget build(BuildContext context) {
return PreferredScaffold(
color: Colors.white,
body: SingleChildScrollView(
controller: controller.scrollController,
primary: false,
... Etc
So, why didn't this work for me? I created a class called "PreferredScaffold" to save myself a few lines of repetitive code.
PreferredScaffold
class PreferredScaffold extends StatelessWidget {
final Widget? body;
final Color? color;
const PreferredScaffold({Key? key, this.body, this.color = Colors.white})
: super(key: key);
#override
Widget build(BuildContext context) {
final GlobalKey<ScaffoldState> scaffoldState = GlobalKey();
return Scaffold(
key: scaffoldState,
backgroundColor: color,
appBar: myNavBar(context, scaffoldState),
drawer: const Drawer(
child: DrawerWidget(),
),
body: body,
);
}
}
The problem with the above is, when the window is adjusted, the build function is called. When the build function is called for the scaffold, the scaffoldKey is being set. When set, it returns the scroll position back to 0, or the top of the screen.
In the end, I had to make another Controller that would basically hand over the same instance of a key to the scaffold, so it wouldn't be reset when the build function was called.
ScaffoldController
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class ScaffoldControllerX extends GetxController {
static ScaffoldControllerX instance = Get.find();
final GlobalKey<ScaffoldState> scaffoldState = GlobalKey();
}
This changed my PreferredScaffold to the following
PreferredScaffold (version 2)
import 'package:flutter/material.dart';
import 'drawer/drawer_widget.dart';
import 'nav/my_navigation_bar.dart';
class PreferredScaffold extends StatelessWidget {
final Widget? body;
final Color? color;
PreferredScaffold({Key? key, this.body, this.color = Colors.white})
: super(key: key);
final ScaffoldControllerX scaffoldControllerX = ScaffoldControllerX();
#override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldControllerX.scaffoldState,
backgroundColor: color,
appBar: NavBar(context, scaffoldControllerX.scaffoldState),
drawer: const Drawer(
child: DrawerWidget(),
),
body: body,
);
}
}
I hope this helps if someone has a similar situation.
I have tackeled without any success with problem...
My main page is stateful class with tabbar with two tabs. First tab has some text from global variables and couple of textfields that also are prefilled with global variables.
Second tab has a button and ontap it calls setstate that changes variables, that are used on first tab and then animates to first tab.
My problem is that first tabs text doesnt change to new value. At the same time textfields will have new values. If i add print command before returning text on first tab, code will print out new values, but state for text is not set, at the same time textfields state will be set.
Its not possible at moment to add code, but i hope i described mu problem good enough.
Thank You!
I tryed many things and now i got strange working solution that makes what i want.
If i just set new variables and after that let tabcontroller to animate dirst page, pages state will not be set, but if i add small delay, then it works like i want. If anyone could explain why, i would be really thankful.
onPressed: () {
setProduct();
Timer(Duration(milliseconds: 100), animateToFirstPage);
}
There is a really elaborate explanation in this answer.
Bottom line, there is a race condition between setState and animateTo, and he suggests breaking it so:
onPressed: () {
setProduct();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
animateToFirstPage;
})
}
Verified it worked for me, and without creepy .sleep solutions
Use a simple state management solution. Where both tabs can listen and modify the values you want. Without code is hard to demonstrate. But you can't simply change the state of a widget from another widget, using provider would be easier.
To update and listen to the change, use StreamController and StreamBuilder. You can put the first tab in a widget combined with SingleTickerProviderStateMixin to prevent it from reloading as well. I created a simple app for demonstration:
Full example:
main.dart
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: MyApp()));
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
final StreamController _streamController = StreamController<String>();
TabController _tabController;
#override
void initState() {
_tabController = TabController(length: 2, vsync: this);
super.initState();
}
#override
void dispose() {
_streamController.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Sample App'),
),
bottomNavigationBar: TabBar(
controller: _tabController,
labelColor: Colors.red,
tabs: [Tab(text: 'Tab 1'), Tab(text: 'Tab 2')],
),
body: TabBarView(
controller: _tabController,
children: [
FirstTab(_streamController),
Container(
child: Center(
child: TextButton(
child: Text('Press me'),
onPressed: () {
final _someText =
'Random number: ' + Random().nextInt(100).toString();
_streamController.add(_someText);
_tabController.animateTo(0);
},
),
),
),
],
),
);
}
}
class FirstTab extends StatefulWidget {
final StreamController _streamController;
FirstTab(this._streamController);
#override
_FirstTabState createState() => _FirstTabState();
}
class _FirstTabState extends State<FirstTab>
with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
return Container(
child: Center(
child: StreamBuilder<String>(
initialData: 'Empty text',
stream: widget._streamController.stream,
builder: (context, snapshot) {
return Text(snapshot.data);
}),
),
);
}
}
just start to study Flutter and I try to create a widget with random color generator, and partly i did it, by I don't know how to add background text to the center of screen. Maybe someone can help me, please?
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: MyPage()));
}
class MyPage extends StatefulWidget {
#override
_MyPageState createState() => new _MyPageState();
}
class _MyPageState extends State<MyPage> {
Random random = new Random();
Color color ;
void changeRandomColor() {
setState(() {
color = Color.fromARGB(
//or with fromRGBO with fourth arg as _random.nextDouble(),
random.nextInt(256),
random.nextInt(256),
random.nextInt(256),
random.nextInt(256),
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body:
InkWell(
onTap: changeRandomColor,
child: Container(
color: color,
),
)
);
}
}
You are doing it right just give Container fullscreen width and height and you're good to go.
child property in Container can add text widget in the center, like
Container( child: Center(child:Text('data')) );
I'm trying to change the active page index via a pagecontroller in Flutter, using the Bloc pattern and its throwing "'_positions.isNotEmpty': ScrollController not attached to any scroll views.".
This is my code:
WelcomeWizardScreen:
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:fluttertest/blocs/wizard/bloc/bloc.dart';
import 'package:fluttertest/screens/wizardsteps/joincongregation.dart';
import 'package:fluttertest/screens/wizardsteps/welcometomapman.dart';
class WelcomeWizardScreen extends StatefulWidget {
#override
_WelcomeWizardScreenState createState() => _WelcomeWizardScreenState();
}
class _WelcomeWizardScreenState extends State<WelcomeWizardScreen> {
final WizardBloc wizardBloc = WizardBloc();
#override
Widget build(BuildContext context) {
// TODO: implement build
return BlocProvider(
builder: (BuildContext context) => WizardBloc(),
child: PageView(
children: <Widget>[WelcomeToMapMan(), JoinCongregation()],
controller: wizardBloc.pageController,
),
);
}
}
WizardBloc:
import 'package:bloc/bloc.dart';
import 'package:flutter/widgets.dart';
import 'wizard_state.dart';
import 'wizard_event.dart';
class WizardBloc extends Bloc<WizardEvent, WizardState> {
int activeStep = 0;
final PageController pageController = PageController(initialPage: 0, keepPage: false, viewportFraction: 0.4);
#override
WizardState get initialState => WelcomeToMapManState();
#override
Stream<WizardState> mapEventToState(
WizardEvent event,
) async* {
if (event is ChangePage)
{
pageController.jumpToPage(event.pageIndex);
}
// TODO: Add Logic
}
Stream<WizardState> _mapJoinCongregationToState() async* {
}
}
One of the screens in the PageView:
class JoinCongregation extends StatelessWidget {
#override
Widget build(BuildContext context) {
final WizardBloc _wizardBloc = BlocProvider.of<WizardBloc>(context);
// TODO: implement build
return Column(
children: <Widget>[
Center(
child: Text("this is step 2"),
),
RaisedButton(
child: Text("back to step 1"),
onPressed: () => {_wizardBloc.dispatch(ChangePage(0))},
)
],
);
}
}
It seems like the PageViewController isn't "attached" to the PageView when it is called on to change pages, but it initalises correctly (on the correct page index).
How can I solve this? I'm fairly new to flutter.
You shouldn't create a PageController in a bloc because a bloc should not be coupled with the UI (theoretically you should be able to reuse your bloc between Flutter and AngularDart). Please refer to https://github.com/felangel/bloc/issues/18 for an example of how you can accomplish this.
when I tap text widget it will rebuild widget, I don't know why
main.dart
import 'package:flutter/material.dart';
import './loading.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("Main Build.");
return MaterialApp(
title: 'chat',
home: LoadingPage(),
);
}
}
loadingPage.dart
import 'package:flutter/material.dart';
import 'signUp/inputPhone.dart';
class LoadingPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("Loading Page Build.");
FocusScope.of(context).requestFocus(new FocusNode());
new Future.delayed(Duration(seconds: 3), () {
print("Flutter APP UI");
Navigator.of(context).pushReplacement(
new MaterialPageRoute(builder: (context) => InputPhone()));
});
return Stack(
fit: StackFit.expand,
alignment: AlignmentDirectional.center,
children: <Widget>[
Image.asset("images/bgPatten.png", fit: BoxFit.cover),
Image.asset("images/logoAll.png"),
],
);
}
}
inputPhone.dart
import 'package:flutter/material.dart';
class InputPhone extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("input phone build");
return GestureDetector(
onTap: () {
print("Tap Outside.");
FocusScope.of(context).requestFocus(new FocusNode());
},
child: Scaffold(
appBar: new AppBar(
title: Text("STEP 1"),
leading: Container(),
elevation: 0.0,
),
body: TextField(),
),
);
}
}
but when I change main.dart home to InputPhone(), it will not happen
sorry I really don't know what's wrong with my code
please give me some help... thank you all... :)
When you tap TextField Widget, it makes the keyboard show up. and When keyboard shows up your screen size is changed. And just because your screen size is changed the widgets that you are using needs to build again.
And this thing will happen with both StatefulWidget and StatelessWidget.
You can verify this behavior by making State of an StatefulWidget extend WidgetsBindingObserver class. The method didChangeMetrics() will be called when your KeyBoard shows up by tapping on TextField.
This happens because of scaffold's resizeToAvoidBottomPadding which is by default true.
Refer following comments from the source code.
/// If true the [body] and the scaffold's floating widgets should size
/// themselves to avoid the onscreen keyboard whose height is defined by the
/// ambient [MediaQuery]'s [MediaQueryData.viewInsets] `bottom` property.
///
/// For example, if there is an onscreen keyboard displayed above the
/// scaffold, the body can be resized to avoid overlapping the keyboard, which
/// prevents widgets inside the body from being obscured by the keyboard.
///
/// Defaults to true.
final bool resizeToAvoidBottomInset;
In StatelessWidget the Build method is being called for many reasons like (screen rotation, animations, scrolling...) so that you should use StatefulWidget