How to get the current offset of Text Span? - flutter

I'm working on a "search in a document" app . I want when I type the word it the custom scroll view automatically scrolls to the searched word.
I split my document to bunch of Text spans. how do I get the offset of only one of them. Is that even possible?
I tried but it doesn't work
with AutomaticKeepAliveClientMixin<WordViewPage> {
#override
bool get wantKeepAlive => true;
String previewedText;
ScrollController scrollController;
List<String> splitted = [''];
Color customColor = Colors.transparent;
#override
void initState() {
super.initState();
loadAsset().then((String loadedString) {
setState(() {
previewedText = loadedString;
splitter();
});
});
}
#override
Widget build(BuildContext context) {
super.build(context);
List<Widget> he = [wordFile()];
return Scaffold(
body: CustomScrollView(
controller: scrollController,
slivers: <Widget>[
SliverAppBar(
automaticallyImplyLeading: false,
title: searchBar(),
centerTitle: true,
backgroundColor: Color(0xfffc3b398),
),
SliverPadding(
padding: EdgeInsets.all(10),
sliver: SliverList(
delegate: SliverChildListDelegate(he),
),
)
],
));
}
Future<String> loadAsset() async {
return await rootBundle.loadString('assets/test.txt');
}

You can extend your class with Search Delegate in flutter
Detail explanation and code : SearchDelegate
or you can see my code and see the RichText() widget
#override
Widget buildResults(BuildContext context){
return new FutureBuilder(
future: _getUser(query),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(
child: Center(
child: CircularProgressIndicator(),
),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) => ListTile(
leading: Icon(Icons.person),
title: Text(snapshot.data[index].fName +
" " +
snapshot.data[index].lName),
title: RichText(
text: TextSpan(
text: suggestionList[index].substring(0, query.length),
style: TextStyle(
color: Colors.black, fontWeight: FontWeight.bold)),
),
onTap: () {
},
),
);
}
},
);
}

Related

Draggable list view flutter

Hi the listview that is displayed below allows you to scroll a series of elements that are shown in a flutter list, the problem is that it is not possible to scroll the entire list, how can I correct this thing?
Flutter code:
import 'package:costal/Model/Maintenance.dart';
import 'package:costal/View/constants/color.dart';
import 'package:costal/View/utils/support.dart';
import 'package:costal/View/widgets/SceduledCard.dart';
import 'package:flutter/material.dart';
import 'package:rflutter_alert/rflutter_alert.dart';
import 'DetailScreen.dart';
class ScheduledScreen extends StatelessWidget {
const ScheduledScreen({Key? key}) : super(key: key);
Future<List<Maintenance>> loadvalues() async {
return await Maintenance.getMaintenanceScheduled();
}
Future<bool> completeDay(BuildContext context, Maintenance man) async {
var check = await man.completeDay();
if (check == true) {
Share.alertCustom(context, AlertType.success, 'Manutenzione Completa', 'Hai completato la manutenzione', true);
} else {
Share.alertCustom(context, AlertType.error, 'Errore', 'non รจ stato possibile completare le manutenzioni', false);
}
return check;
}
Widget getLoader() {
return const Align(
alignment: Alignment.center,
child: CircularProgressIndicator(
value: 50,
semanticsLabel: 'Loading value',
),
);
}
#override
Widget build(BuildContext context) {
return FutureBuilder<List<Maintenance>>(
future: loadvalues(),
builder: (BuildContext context, AsyncSnapshot<List<Maintenance>> snapshot) {
if (!snapshot.hasData) {
return getLoader();
} else {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text(
'Manutenzioni',
style: TextStyle(
fontFamily: 'Poppins',
color: kPrimaryColor,
),
),
leading: CircleAvatar(
radius: 20,
backgroundColor: const Color(0xff94d500),
child: IconButton(
icon: const Icon(Icons.access_alarms_sharp),
onPressed: () async {
completeDay(context, snapshot.data![0]);
},
),
),
),
body: Wrap(
//the magic
children: [
Container(
padding: const EdgeInsets.all(20),
child: ListView.separated(
shrinkWrap: true,
itemCount: snapshot.data!.length,
separatorBuilder: (context, index) {
return const SizedBox(
height: 10,
);
},
itemBuilder: (context, index) {
return GestureDetector(
onTap: (() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailScreen(snapshot.data![index])),
);
}),
child: SceduledCard(man: snapshot.data![index], c: Colors.blue),
);
}))
],
),
);
}
});
}
}

Weird scrolling behaviour when use ScrollController + FutureBuilder + Provider + ListView.builder

I created a list view based on a Future Provider. It works as expected.
Now I want to add a ScrollController in order to create a animated FloatingActionButton like Gmail "Compose" button.
I put controller attribute on listView.builder.
And here I have weird behaviour : I can't scroll. As soon as I scroll down or up listview is rebuilding and I can't perform any scroll.
Here my code :
ScrollController _scrollController = ScrollController();
bool isFAB = false;
#override
void initState() {
_scrollController.addListener(() {
if (_scrollController.offset > 50) {
setState(() {
isFAB = true;
});
} else {
setState(() {
isFAB = false;
});
}
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.toolListTitle),
),
body: FutureBuilder(
future:
Provider.of<MyTools>(context, listen: false).fetchAndSetTools(),
builder: (ctx, snapshot) => snapshot.connectionState ==
ConnectionState.waiting
? const Center(
child: CircularProgressIndicator(),
)
: Consumer<MyTools>(
child: Center(
child: Text(AppLocalizations.of(context)!.noToolYet),
),
builder: (ctx, myTools, ch) => myTools.items.isEmpty
? Center(
child: Text(AppLocalizations.of(context)!.noToolYet),
)
: ListView.builder(
controller: _scrollController,
scrollDirection: Axis.vertical,
itemCount: myTools.items.length,
itemBuilder: (ctx, i) => ToolWidget(
id: myTools.items[i].id,
name: myTools.items[i].name,
createdAt: myTools.items[i].createdAt,
description: myTools.items[i].description,
),
),
),
),
floatingActionButton: isFAB
? FloatingActionButton(
onPressed: () =>
Navigator.of(context).pushNamed(AddToolScreen.routeName),
child: Icon(
Icons.add_sharp,
color: Theme.of(context).primaryColor,
),
backgroundColor: Colors.black,
)
: FloatingActionButton.extended(
onPressed: () =>
Navigator.of(context).pushNamed(AddToolScreen.routeName),
icon: Icon(
Icons.add_sharp,
color: Theme.of(context).primaryColor,
),
backgroundColor: Colors.black,
label: Text(
"Add Tool",
style: TextStyle(
color: Theme.of(context).primaryColor,
),
),
));
}
}
Can you help me ?
Thanks
I think the
setState(() {
isFAB = true;
});
in your _scrollController.addListener function is resetting the scroll position to the top again.

Widget ordering but how

Here is my code:
#override
Widget build(BuildContext context) {
return FutureBuilder<Categories>(
future: _futureCategories,
builder: (BuildContext context, AsyncSnapshot<Categories> snapshot) {
if (snapshot.hasData) {
final name = snapshot.data?.data;
return DefaultTabController(
length: 1,
child: Scaffold(
body: ListView.builder(
itemCount: name?.length,
itemBuilder: (BuildContext context, int index) {
return Text(
' ${name?[index].name}',
);
},
),
appBar: AppBar(
title: const Text('List User Example'),
bottom: TabBar(
indicatorColor: Colors.lime,
tabs: [
Text(' ${name?[0].name}'.toUpperCase()),
],
isScrollable: true,
labelColor: Colors.black,
),
),
),
);
} else if (snapshot.hasError) {
return NewsError(
errorMessage: '${snapshot.hasError}',
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
);
}
}
This code fetching categories from JSON and uses them on Tabcontroller. The problem is in this current code I cannot access index value.
I am newbie and trying to understand the widget system of Flutter. How can I use Tabcontroller to use int index value on TabController?
return Text(' ${name?[index].name}',);
change
return Text(' ${snapshot.data.name}',);

Passing QuerySnapshot data to TabView so I can access it in pages in each tab

I have added a TabBar to my flutter app. It was pretty easy. I have created a new page for tab 1 and moved an existing page to tab 2. Both of these pages should display data from a Firestore QuerySnapshot but I don't know how to do this.
I have a screen, AgentDashBoardScreen, that creates a QuerySnapshot and builds a ListView with the data retrieved by the query.
In the onTap property of each ListTile the user is taken to the TransactionHomeScreen which has the TabBar widget in it. Also, I want to pass the QuerySnapshot to this page. Below is the code for AgentDashBoardScreen.
class AgentDashboardScreen extends StatefulWidget {
static const String id = 'agent_dashboard_screen';
#override
_AgentDashboardScreenState createState() => _AgentDashboardScreenState();
}
class _AgentDashboardScreenState extends State<AgentDashboardScreen> {
bool showSpinner = false;
//int _selectedIndex = 0;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('assets/images/Appbar_logo.png',
fit: BoxFit.cover, height: 56),
],
),
),
drawer: Drawer(
child: ListView(
children: <Widget>[
ListTile(
title: Text("Agent Profile"),
onTap: () {
MainScreen.of(context)?.setIndex(3); // Added this for BottomNavigationBar sync
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => AgentProfileScreen()));
},
),
],
),
),
body: SafeArea(
child: Container(
child: StreamBuilder(
stream: FirestoreService().getAgencyTrxns(context),
builder: (context, snapshot) {
if (!snapshot.hasData)
return Center(
child: const Text(
'Loading...',
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
));
return new ListView.builder(
itemCount: (snapshot.data! as QuerySnapshot).docs.length,
itemBuilder: (BuildContext context, int index) {
Trxns trxns = Trxns.fromFirestore(
(snapshot.data! as QuerySnapshot).docs[index]
.data() as Map<String, dynamic>);
return ListTile(
isThreeLine: true,
title: Text(
'Client: ${trxns.clientFName ?? 'n/a'} ${trxns
.clientLName ?? 'n/a'}',
style: TextStyle(
fontWeight: FontWeight.w900,
color: Colors.blueAccent),
),
subtitle: Text.rich(TextSpan(
text:
'${trxns.propertyAddress ?? 'n/a'}, ${trxns
.propertyCity ?? 'n/a'}, ${trxns
.propertyState ?? 'n/a'}',
children: <TextSpan>[
TextSpan(
text:
'\nPrice: ${trxns!.contractPrice == null ? 'n/a' : NumberFormat.simpleCurrency().format(trxns.contractPrice)}\nStatus: ${trxns.trxnStatus ?? 'n/a'}',
style: TextStyle(
fontWeight: FontWeight.w900,
color: Colors.blueGrey),
)
])),
trailing: Text('MLS#: ${trxns.mlsNumber ?? 'n/a'}\n${trxns.clientType}'),
onTap: () {
MainScreen.of(context)?.setIndex(2); // Added this for BottomNavigationBar sync
globals.newTrxn = false;
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
**TrxnHomeScreen(
(snapshot.data! as QuerySnapshot).docs[index])));**
},
);
});
},
),
),
),
));
}
}
Here is the code for the TrxnHomeScreen.
class TrxnHomeScreen extends StatefulWidget {
static const String id = 'trxn_home_screen';
final QueryDocumentSnapshot? trxns;
TrxnHomeScreen([this.trxns]);
#override
_TrxnHomeScreenState createState() => _TrxnHomeScreenState();
}
class _TrxnHomeScreenState extends State<TrxnHomeScreen> with SingleTickerProviderStateMixin {
bool showSpinner = false;
TabController? _trxnTabController;
#override
void initState() {
super.initState();
_trxnTabController = TabController(length: 3, vsync: this);
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('assets/images/Appbar_logo.png',
fit: BoxFit.cover, height: 56),
],
),
bottom: TabBar(
indicatorWeight: 5,
indicatorColor: Colors.orange,
controller: _trxnTabController,
tabs: [
Tab(
text: "STATUS"
),
Tab(
text: "DETAILS",
),
Tab(
text: "TASKS",
),
],
),
),
body: SafeArea(
child: Container(
child: TabBarView(
controller: _trxnTabController,
children: <Widget>[
TransactionStatusScreen(widget.trxns),
TransactionDetailScreen(widget.trxns),
Text('Tasks'),
])
),
),
)),
);
}
}
Here is the code for the TransactionDetailScreen.
class TransactionDetailScreen extends StatefulWidget {
static const String id = 'transaction_detail_screen';
final QueryDocumentSnapshot? trxns;
TransactionDetailScreen([this.trxns]);
#override
_TransactionDetailScreenState createState() =>
_TransactionDetailScreenState();
}
class _TransactionDetailScreenState extends State<TransactionDetailScreen> {
getTrxn() async {
final DocumentSnapshot _mlsId =
await mlsRef.doc(globals.mlsId).get();
_mlsSearchLink = _mlsId.get('mlsNbrSearch');
if (widget.trxns == null) {
globals.newTrxn = true;
} else {
globals.newTrxn = false;
}
#override
void initState() {
getTrxn();
super.initState();
}
#override
Widget build(BuildContext context) {
}
}
From the code, you are passing the QueryDocumentSnapshot to TransactionHomeScreen but the code you provided for the tab bar is TrxnHomeScreen.

Problem with using icons from cloud firestore in flutter app

I am currently displaying a list of icons in an Alert Dialog within a control_point Daily Button. I want to display all of the buttons I have in my cloud firestore within the Alert Dialog but due to the way I have laid out the code, this means that for each icon in the database, the Daily Goals widget is repeated, as shown in the image below:
Edit: The second image is the desired layout
If you click on the first control point button, it shows the first icon that is in the database and it is repeated 4 times. Then if you click the second control point button, it shows the next icon that is in the database as shown in the images below. This is repeated for all of the icons in the database. I am wanting to have all of the icons in one Alter Dialog.
This is my code:
class _MyHomePageState3 extends State<MyHomePage3> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF030164),
appBar: AppBar(centerTitle: true, title: Text('Project')),
body: _buildBody(context),
);
}
Widget _buildBody(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection('icons').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return _buildList(context, snapshot.data.docs);
},
);
}
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return ListView(
children: snapshot.map((data) => _buildListItem(context, data)).toList(),
);
}
Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
final record3 = Record3.fromSnapshot(data);
int iconCode = record3.ref;
List<IconData> _iconsDaily = [
Icons.shopping_cart,
Icons.cake_rounded,
Icons.card_giftcard,
Icons.control_point,
];
return Padding(
child: new Container(
child: new ListView(
children: <Widget>[
new Text(
"Daily Goals",
),
new Container(
child: new ListView(
scrollDirection: Axis.horizontal,
children: new List.generate(4, (int index) {
return new Positioned(
child: new DailyButton(
onTap: () {
if (_iconsDaily[index] == Icons.control_point) {
showDialog(
context: context,
builder: (BuildContext context) =>
_buildPopupDialog(context, data),
);
}
},
iconData: _iconsDaily[index]
),
);
}),
),
),
],
),
),
);
}
Widget _buildPopupDialog(BuildContext context, DocumentSnapshot data) {
final record3 = Record3.fromSnapshot(data);
int iconCode = record3.ref;
return new AlertDialog(
content: SingleChildScrollView(
child: new Container(
child: GridView.count(
children: new List.generate(4, (int index) {
return new Positioned(
child: new MoodButton(
onTap: () => print("Mood"),
iconData: (IconData(iconCode, fontFamily: 'MaterialIcons')),
),
);
}),
),
),
),
);
}
}
class Record3 {
final String name;
final int ref;
final bool display;
final DocumentReference reference;
Record3.fromMap(Map<String, dynamic> map, {this.reference})
: assert(map['name'] != null),
assert(map['ref'] != null),
assert(map['display'] != null),
name = map['name'],
ref = map['ref'],
display = map['display'];
Record3.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data(), reference: snapshot.reference);
#override
String toString() => "Record<$name:$ref:$display>";
}
If anyone would be able to give any advice for restructing the code so that it only shows one Daily Goals and would be able to include all of the icons within the alert dialog, that would be amazing!