I want to develop a more advanced version of the ListView widget as a package. This is my first package.
I wrote a code like this:
modern_card_listview.dart:
library modern_card_listview;
import 'package:flutter/material.dart';
class CardListView extends StatefulWidget {
List items;
CardListView({Key? key, required this.items}) : super(key: key);
#override
State<CardListView> createState() => _CardListViewState();
}
class _CardListViewState extends State<CardListView> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ListView.builder(
itemCount: widget.items.length,
itemBuilder: ((context, index) {
return Card(
child: ListTile(
title: widget.items[index],
),
);
}),
),
),
);
}
}
I guess I need to use the dart file in the test folder to test the code.
modern_card_listview_test.dart:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:modern_card_listview/modern_card_listview.dart';
CardListView _listView = CardListView(
items: const ["Bu bir", "Bu iki", "Bu uc", "Bu dort", "Bu bes"]);
void main() {
runApp(const App());
}
class App extends StatelessWidget {
const App({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: _listView,
),
);
}
}
How can I try the package I wrote? When I ran the code above, No tests were found. I get an error.This is my first pack and I'm still new. I would be grateful if you help.
I couldn't find a solution
Related
I want to display a paragraph in HTML that has various bold text and links within the tag using flutter on a desktop platform. I've come across "flutter_html", but the only supported platforms are for iOS and Android. Is there any other library that could display HTML text in a desktop environment? If there are none, are there any other ways to make this possible?
You can use flutter_html in desktop app.
Try my code below:
import 'package:html/dom.dart' as dom;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:html/parser.dart' show parse;
import 'package:flutter_html/flutter_html.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.yellow,
),
home: const HomePage(title: 'This Is Desktop'),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
HomePageState createState() => HomePageState();
}
class HomePageState extends State<HomePage> {
#override
void initState() {
parseHtml();
super.initState();
}
Future<dom.Document> parseHtml() async {
return parse(await rootBundle.loadString('static/index.html'));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: FutureBuilder<dom.Document>(
future: parseHtml(),
builder:
(BuildContext context, AsyncSnapshot<dom.Document> snapshot) {
if (snapshot.hasData) {
return Html(data: snapshot.data?.outerHtml);
} else {
return const Center(
child: Text("Loading"),
);
}
}),
);
}
}
However, some tag has no effect. For example img tag not showing image, a tag only have link style but won't open a browser to a link when you click on it.
And some of css styles are not support, like flex.
i tried to check which platform that the app been used in flutter. followed this kIsWeb reference for checking it. this is the code that i used
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class ResponsiveLayout extends StatelessWidget {
final Widget mobileScreenLayout;
final Widget webScreenLayout;
const ResponsiveLayout({
Key? key,
required this.mobileScreenLayout,
required this.webScreenLayout,
}) : super(key: key);
#override
Widget build(BuildContext context) {
if (kIsWeb) {
//WEB SCREEN
return webScreenLayout;
}
//MOBILE SCREEN
return mobileScreenLayout;
}
}
the result was backward. when i run it on web it gave me the mobileScreenLayout and when i run it on emulator it gave me the webScreenLayout. tried to swap both webScreenLayout with the mobileScreenLayout and they work how supposedly with what i want. is there any problem with that or it's ok?
this the link i saw the kIsWeb
https://stackoverflow.com/a/57965689/18627424
Please check mobileScreenLayout and webScreenLayout. I think you added wrong text in these widgets by mistake. kIsWeb will definitely show web when you run it in web. You can also try
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text(
(kIsWeb)?'Hello World Web!':"hello world mobile",
style: Theme.of(context).textTheme.headline4,
);
}
}
I am using GetX plugin to navigate between the pages. And when I navigate back to the page which has Text input field I get 'Duplicate GlobalKey detected in widget tree'
Main Page
import 'package:flutter/material.dart';
import 'package:flutter_application_1/binding.dart';
import 'package:flutter_application_1/controller.dart';
import 'package:flutter_application_1/next.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart';
void main() async {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Kids Game',
debugShowCheckedModeBanner: false,
home: const HomePage(),
initialBinding: HomeBinding(),
builder: EasyLoading.init(),
);
}
}
class HomePage extends GetView<HomeController> {
const HomePage({Key? key}) : super(key: key);
static final formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
Form(
key: formKey,
child: TextFormField(
textAlign: TextAlign.center,
autocorrect: false,
controller: controller.editCtrl,
)),
InkWell(
onTap: () {
Get.to(() => NextPage())
},
child: Text("Next"),
)
],
),
);
}
}
NextPage Widget
import 'package:flutter/material.dart';
import 'package:flutter_application_1/controller.dart';
import 'package:flutter_application_1/main.dart';
import 'package:get/get.dart';
class NextPage extends StatelessWidget {
final homeCtrl = Get.find<HomeController>();
NextPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Text("this is next page"),
InkWell(
onTap: () {
Get.offAll(() => HomePage(), transition: Transition.downToUp);
},
child: const Text("Go Back"),
),
],
),
),
);
}
}
Controller.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class HomeController extends GetxController {
//GlobalKey<FormState> formKey = GlobalKey<FormState>();
final editCtrl = TextEditingController();
}
Binding
import 'package:flutter_application_1/controller.dart';
import 'package:get/get.dart';
class HomeBinding implements Bindings {
#override
void dependencies() {
Get.lazyPut(
() => HomeController(),
);
}
}
When I navigate to NextPage, and then back to HomePage I get the error
Duplicate GlobalKey detected in widget tree.
I read a few different posts, and people had recommended using static with formkey as that has resolved the issue for them so I tried doing the same but it didn't work for me.
When leaving the page with Getx use one of these options, depending on your routing solution:
Get.offAndToNamed('next_screen')
or
Get.off(NextScreen());
This removes the current page from the navigation stack and thus the error Duplicate GlobalKey detected in widget tree should be gone for good when reentering the page.
Source:
https://github.com/jonataslaw/getx/blob/master/documentation/en_US/route_management.md
The issue got resolved. I did the following, in case someone else is facing the issue. I declared the key as private and change the variable from final.
GlobalKey<FormState> _formKey = GlobalKey<FormState>();
And based on a lot of answers I have read, the key is for UI layer so it shouldn't be declared in the controller.
Just change the form key for each widget. Forexample: Just use globalFormKey1 for the first widget and GlobalFormKey2 for the second one. This will not give the error of duplications.
Question:
How to call methodA() from onPressed() of IconButton.
I've tryed to do this by using GlobalKey:
GlobalKey<_MyButtonState> globalKey = GlobalKey();
But it's returns an error.
I have read many forums on this and I have tried all the solutions posed but none of them are working for me.
CODE:
main.dart
import 'package:flutter/material.dart';
import 'button.dart';
void main() {
runApp(MaterialApp( title: 'My app', home: MyApp(),));
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.help),
onPressed: () {
// how can I call methodA from here?
},
),
),
body: HomePage(),
),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Center(
child: MyButton(),
);
}
}
button.dart
import 'package:flutter/material.dart';
class MyButton extends StatefulWidget {
#override
_MyButtonState createState() => _MyButtonState();
}
class _MyButtonState extends State<MyButton> {
#override
Widget build(BuildContext context) {
return Container( );
}
void methodA(){
print('methodA');
}
}
I have read many forums on this and I have tried all the solutions posed but none of them are working for me.
first, you will have to import the file as a package in main.dart:
Main.dart: (just writing the way to import the file)
import 'package:prioject_name/file_name.dart';
Note: this is for files under lib directory.
if your file is under a different directory inside lib
then add the path accordingly,
eg: Button.dart is inside the widgets folder inside the lib folder:
lib
|____widgets
|____Button.dart
then the import statement will be as follows:
import 'package:prioject_name/widgets/Button.dart';
Then try your global key method to call the function:
If it is still not working then you can use my method,
how I call methods from different class in onPressed or onTapped:
your Button.dart file.
import 'package:flutter/material.dart';
// changed the method definition class
class MyButton extends StatefulWidget {
void methodA(){
print('methodA');
}
#override
_MyButtonState createState() => _MyButtonState();
}
class _MyButtonState extends State<MyButton> {
#override
Widget build(BuildContext context) {
...
widget.methodA(); // this would call the method A, anywhere inside the Widget build() function.
return Container( );
}
}
Now in Main.dart:
import 'package:prioject_name/Button.dart';
//call the function here using className().functioName();
....
onPressed(){
MyButton().methodA();
}
Take a look at the InheritedWidget class (and watch the videos).
Base class for widgets that efficiently propagate information down the
tree.
You can look at creating an InheritedWidget that contains a ValueNotifier.
class MyInheritedWidget extends InheritedWidget {
final ValueNotifier<int> buttonTapCountNotifier;
const MyInheritedWidget({
Key key,
#required this.buttonTapCountNotifier,
#required Widget child,
}) : assert(child != null),
super(key: key, child: child);
static MyInheritedWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
}
}
Your MyButton class can call MyInheritedWidget.of(context).buttonTapCountNotifier to get hold of the ValueNotifier and add a listener to it.
Each time the ValueNotifier notifies your MyButton class that the value has been incremented, you can execute methodA.
You could use the Provider package which is quite the preferred method to manage state in Flutter apps. This will help you as well in organizing and growing the app in a clever way.
Take a look at the working code below.
define a ChangeNotifier (PressedProvider) which will save
current state of the app in a unique location and the behavior of your onPress function
you wrap your app
with a ChangeNotifierProvider widget
you wrap the receiving
Widget with a Consumer
you get the Provider.of() when you
need to do something and call a method on it
it will notify the Consumer of a change
Code:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(ChangeNotifierProvider<PressedProvider>( // 2
create: (_) => PressedProvider(),
child: MyApp(),
));
}
class PressedProvider extends ChangeNotifier { // 1
void pressButton() {
print("pressButton");
notifyListeners();
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
leading: Consumer<PressedProvider>( // 3
builder: (_, provider, widget) => IconButton(
icon: Icon(Icons.help),
onPressed: () {
provider.pressButton();
},
),
),
),
body: Center(
child: MyButton(),
),
),
);
}
}
class MyButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
PressedProvider provider = Provider.of<PressedProvider>(context); // 4
return Center(
child: RawMaterialButton(
child: Text("Press me"),
onPressed: () => provider.pressButton()),
);
}
}
could you please show me how can I notify my statefull child widget that somewhere in parent user clicks on button?
I have two separate .dart files
in the first file I described main screen widget with FAB
and in the second one I have ListWidget (like RecyclerView)
If user tap on FAB I want notify my ListWidget about it so it can e.g. add one more item.
I have java/android background but it's quite hard for me to change my mind flow.
The first option would be to build the child widget each time you add an item to the list, passing the list as a parameter to the child.
But using streams is a nice way to avoid rebuilding the child widget each time. I think the following code is a good starting point (You could also use a StreamBuilder to build the list leveraging the stream).
In main.dart
import 'dart:async';
import 'package:base_test_project/expanding_list.dart';
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
StreamController<int> _controller = StreamController<int>();
int _number = 0;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new ExpandingList(stream: _controller.stream),
),
floatingActionButton: new FloatingActionButton(
onPressed: () {_controller.add(_number++);},
child: new Icon(Icons.add),
),
);
}
}
In expanding_list.dart
import 'dart:async';
import 'package:flutter/material.dart';
class ExpandingList extends StatefulWidget {
Stream<int> stream;
ExpandingList({this.stream});
#override
_ExpandingListState createState() => _ExpandingListState();
}
class _ExpandingListState extends State<ExpandingList> {
List<int> _myList = [];
#override
void initState() {
super.initState();
widget.stream.listen((number) {
setState(() { _myList.add(number); });
});
}
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: _myList.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.all(15.0), child: Text("Item ${_myList[index]}"));
});
}
}